Ir al contenido

publicidad
publicidad

Foto

Curso MM: 16 Juego de ejemplo: Meteor Defense


Este tema ha sido archivado. Esto significa que no puedes responder en este tema.
No hay respuestas en este tema

#1

Escrito 18 agosto 2009 - 22:03

Juego de ejemplo: Meteor Defense
MeteorDefense.h , MeteorDefense.cpp

Mejorando los sprites en el motor del juego

Una de las cosas que no puede hacer el motor del juego es utilizar un sprite durante un ciclo del juego y luego eliminarlo. Por ejemplo una explosión. La clave para conseguir hacerlo se basa en añadir dos variables: BOOL m_bDying y BOOL m_bOneCycle. La primera indica si un sprite ha sido marcado como "muriendo". La segunda variable indica si un sprite sólo se va a animar una vez con todos sus frames y luego desaparecer. Las dos variables se inicializan como FALSE, es decir, que los sprites se comportarán de forma normal.

En el método Kill() se puede poner el valor TRUE a m_bDying. Éste es un método de acceso que sólo hace eso:

[code:1]void Kill() { m_bDying = TRUE; };[/code]

Con éste método tenemos una forma fácil y eficiente de matar un sprite con la seguridad de que se hará de la forma adecuada dentro del motor del juego. Ésta es una forma directa para algunas situaciones. Otra forma más elegante es que el mismo sprite se autodestruya. Ahora el método UpdateFrame() soporta ésta característica examinando la variable m_bOneCycle y luego estableciendo la variable m_bDying según sea necesario.

En SetNumFrames() ahora pasamos como parámetro bOneCycle:

[code:1]void SetNumFrames( int iNumFrames, BOOL bOneCycle = FALSE);[/code]

Ahora en Sprite::Update() es cuando se marca al sprite como "muriendo".

[code:1]SPRITEACTION Sprite::Update()
{
// See if the sprite needs to be killed
if (m_bDying)
return SA_KILL;

// Update the frame
UpdateFrame();

...
}[/code]

De ésta manera eliminamos el sprite, pero es muy interesante saber cuándo se ha eliminado uno, por ejemplo para crear una explosión. La notificación de la muerte de un sprite se hace a través de la función SpriteDying(Sprite* pSprite). Ésta no viene con el motor del juego, hay que incluírla en cada juego que creemos para hacer lo que haya que hacer al eliminar un sprite. Por lo tanto ahora en el motor, en UpdateSprites() hay una llamada a SpriteDying().

Construyendo el juego

En la cabecera se incluye la definición de la función NewGame() que sirve para inicializar el juego. GameStart() inicializa los elementos básicos y luego llama a NewGame() que se encarga de empezar un juego cuando GameStart() ya ha hecho su trabajo. La función AddMeteor() simplifica la tarea de añadir un meteorito.

GamePaint() dibuja el fondo, el bitmap del suelo, los sprites, la puntuación y el mensaje de fin de juego. En SpriteCollision() vemos el uso de Kill().

La función más interesante es SpriteDying():

[code:1]void SpriteDying(Sprite* pSprite)
{
// See if a meteor sprite is dying
if (pSprite->GetBitmap() == _pMeteorBitmap)
{
// Play the explosion sound
PlaySound((LPCSTR)IDW_EXPLODE, _hInstance, SND_ASYNC |
SND_RESOURCE | SND_NOSTOP);

// Create an explosion sprite at the meteor's position
RECT rcBounds = { 0, 0, 600, 450 };
RECT rcPos = pSprite->GetPosition();
Sprite* pSprite = new Sprite(_pExplosionBitmap, rcBounds);
pSprite->SetNumFrames(12, TRUE);
pSprite->SetPosition(rcPos.left, rcPos.top);
_pGame->AddSprite(pSprite);
}
}[/code]

La función AddMeteor() también es interesante. Se crean meteoritos de forma aleatoria y se calcula su velocidad para que apunten a una ciudad.

MeteorDefense.cpp
[code:1]//-----------------------------------------------------------------
// Meteor Defense Application
// C++ Source - MeteorDefense.cpp
//-----------------------------------------------------------------

//-----------------------------------------------------------------
// Include Files
//-----------------------------------------------------------------
#include "MeteorDefense.h"

//-----------------------------------------------------------------
// Game Engine Functions
//-----------------------------------------------------------------
BOOL GameInitialize(HINSTANCE hInstance)
{
// Create the game engine
_pGame = new GameEngine(hInstance, TEXT("Meteor Defense"),
TEXT("Meteor Defense"), IDI_METEORDEFENSE, IDI_METEORDEFENSE_SM, 600, 450);
if (_pGame == NULL)
return FALSE;

// Set the frame rate
_pGame->SetFrameRate(30);

// Store the instance handle
_hInstance = hInstance;

return TRUE;
}

void GameStart(HWND hWindow)
{
// Seed the random number generator
srand(GetTickCount());

// Create the offscreen device context and bitmap
_hOffscreenDC = CreateCompatibleDC(GetDC(hWindow));
_hOffscreenBitmap = CreateCompatibleBitmap(GetDC(hWindow),
_pGame->GetWidth(), _pGame->GetHeight());
SelectObject(_hOffscreenDC, _hOffscreenBitmap);

// Create and load the bitmaps
HDC hDC = GetDC(hWindow);
_pGroundBitmap = new Bitmap(hDC, IDB_GROUND, _hInstance);
_pTargetBitmap = new Bitmap(hDC, IDB_TARGET, _hInstance);
_pCityBitmap = new Bitmap(hDC, IDB_CITY, _hInstance);
_pMeteorBitmap = new Bitmap(hDC, IDB_METEOR, _hInstance);
_pMissileBitmap = new Bitmap(hDC, IDB_MISSILE, _hInstance);
_pExplosionBitmap = new Bitmap(hDC, IDB_EXPLOSION, _hInstance);
_pGameOverBitmap = new Bitmap(hDC, IDB_GAMEOVER, _hInstance);

// Create the starry background
_pBackground = new StarryBackground(600, 450);

// Play the background music
_pGame->PlayMIDISong(TEXT("Music.mid"));

// Start the game
NewGame();
}

void GameEnd()
{
// Close the MIDI player for the background music
_pGame->CloseMIDIPlayer();

// Cleanup the offscreen device context and bitmap
DeleteObject(_hOffscreenBitmap);
DeleteDC(_hOffscreenDC);

// Cleanup the bitmaps
delete _pGroundBitmap;
delete _pTargetBitmap;
delete _pCityBitmap;
delete _pMeteorBitmap;
delete _pMissileBitmap;
delete _pExplosionBitmap;
delete _pGameOverBitmap;

// Cleanup the background
delete _pBackground;

// Cleanup the sprites
_pGame->CleanupSprites();

// Cleanup the game engine
delete _pGame;
}

void GameActivate(HWND hWindow)
{
// Resume the background music
_pGame->PlayMIDISong(TEXT(""), FALSE);
}

void GameDeactivate(HWND hWindow)
{
// Pause the background music
_pGame->PauseMIDISong();
}

void GamePaint(HDC hDC)
{
// Draw the background
_pBackground->Draw(hDC);

// Draw the ground bitmap
_pGroundBitmap->Draw(hDC, 0, 398, TRUE);

// Draw the sprites
_pGame->DrawSprites(hDC);

// Draw the score
TCHAR szText[64];
RECT rect = { 275, 0, 325, 50 };
wsprintf(szText, "%d", _iScore);
SetBkMode(hDC, TRANSPARENT);
SetTextColor(hDC, RGB(255, 255, 255));
DrawText(hDC, szText, -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);

// Draw the game over message, if necessary
if (_bGameOver)
_pGameOverBitmap->Draw(hDC, 170, 150, TRUE);
}

void GameCycle()
{
if (!_bGameOver)
{
// Randomly add meteors
if ((rand() % _iDifficulty) == 0)
AddMeteor();

// Update the background
_pBackground->Update();

// Update the sprites
_pGame->UpdateSprites();

// Obtain a device context for repainting the game
HWND hWindow = _pGame->GetWindow();
HDC hDC = GetDC(hWindow);

// Paint the game to the offscreen device context
GamePaint(_hOffscreenDC);

// Blit the offscreen bitmap to the game screen
BitBlt(hDC, 0, 0, _pGame->GetWidth(), _pGame->GetHeight(),
_hOffscreenDC, 0, 0, SRCCOPY);

// Cleanup
ReleaseDC(hWindow, hDC);
}
}

void HandleKeys()
{
}

void MouseButtonDown(int x, int y, BOOL bLeft)
{
if (!_bGameOver && bLeft)
{
// Create a new missile sprite and set its position
RECT rcBounds = { 0, 0, 600, 450 };
int iXPos = (x < 300) ? 144 : 449;
Sprite* pSprite = new Sprite(_pMissileBitmap, rcBounds, BA_DIE);
pSprite->SetPosition(iXPos, 365);

// Calculate the velocity so that it is aimed at the target
int iXVel, iYVel = -6;
y = min(y, 300);
iXVel = (iYVel * ((iXPos + 8) - x)) / (365 - y);
pSprite->SetVelocity(iXVel, iYVel);

// Add the missile sprite
_pGame->AddSprite(pSprite);

// Play the fire sound
PlaySound((LPCSTR)IDW_FIRE, _hInstance, SND_ASYNC |
SND_RESOURCE | SND_NOSTOP);

// Update the score
_iScore = max(--_iScore, 0);
}
else if (_bGameOver && !bLeft)
// Start a new game
NewGame();
}

void MouseButtonUp(int x, int y, BOOL bLeft)
{
}

void MouseMove(int x, int y)
{
// Track the mouse with the target sprite
_pTargetSprite->SetPosition(x - (_pTargetSprite->GetWidth() / 2),
y - (_pTargetSprite->GetHeight() / 2));
}

void HandleJoystick(JOYSTATE jsJoystickState)
{
}

BOOL SpriteCollision(Sprite* pSpriteHitter, Sprite* pSpriteHittee)
{
// See if a missile and a meteor have collided
if ((pSpriteHitter->GetBitmap() == _pMissileBitmap &&
pSpriteHittee->GetBitmap() == _pMeteorBitmap) ||
(pSpriteHitter->GetBitmap() == _pMeteorBitmap &&
pSpriteHittee->GetBitmap() == _pMissileBitmap))
{
// Kill both sprites
pSpriteHitter->Kill();
pSpriteHittee->Kill();

// Update the score
_iScore += 6;
_iDifficulty = max(50 - (_iScore / 10), 5);
}

// See if a meteor has collided with a city
if (pSpriteHitter->GetBitmap() == _pMeteorBitmap &&
pSpriteHittee->GetBitmap() == _pCityBitmap)
{
// Play the big explosion sound
PlaySound((LPCSTR)IDW_BIGEXPLODE, _hInstance, SND_ASYNC |
SND_RESOURCE);

// Kill both sprites
pSpriteHitter->Kill();
pSpriteHittee->Kill();

// See if the game is over
if (--_iNumCities == 0)
_bGameOver = TRUE;
}

return FALSE;
}

void SpriteDying(Sprite* pSprite)
{
// See if a meteor sprite is dying
if (pSprite->GetBitmap() == _pMeteorBitmap)
{
// Play the explosion sound
PlaySound((LPCSTR)IDW_EXPLODE, _hInstance, SND_ASYNC |
SND_RESOURCE | SND_NOSTOP);

// Create an explosion sprite at the meteor's position
RECT rcBounds = { 0, 0, 600, 450 };
RECT rcPos = pSprite->GetPosition();
Sprite* pSprite = new Sprite(_pExplosionBitmap, rcBounds);
pSprite->SetNumFrames(12, TRUE);
pSprite->SetPosition(rcPos.left, rcPos.top);
_pGame->AddSprite(pSprite);
}
}

//-----------------------------------------------------------------
// Functions
//-----------------------------------------------------------------
void NewGame()
{
// Clear the sprites
_pGame->CleanupSprites();

// Create the target sprite
RECT rcBounds = { 0, 0, 600, 450 };
_pTargetSprite = new Sprite(_pTargetBitmap, rcBounds, BA_STOP);
_pTargetSprite->SetZOrder(10);
_pGame->AddSprite(_pTargetSprite);

// Create the city sprites
Sprite* pSprite = new Sprite(_pCityBitmap, rcBounds);
pSprite->SetPosition(2, 370);
_pGame->AddSprite(pSprite);
pSprite = new Sprite(_pCityBitmap, rcBounds);
pSprite->SetPosition(186, 370);
_pGame->AddSprite(pSprite);
pSprite = new Sprite(_pCityBitmap, rcBounds);
pSprite->SetPosition(302, 370);
_pGame->AddSprite(pSprite);
pSprite = new Sprite(_pCityBitmap, rcBounds);
pSprite->SetPosition(490, 370);
_pGame->AddSprite(pSprite);

// Initialize the game variables
_iScore = 0;
_iNumCities = 4;
_iDifficulty = 50;
_bGameOver = FALSE;

// Play the background music
_pGame->PlayMIDISong();
}

void AddMeteor()
{
// Create a new meteor sprite and set its position
RECT rcBounds = { 0, 0, 600, 390 };
int iXPos = rand() % 600;
Sprite* pSprite = new Sprite(_pMeteorBitmap, rcBounds, BA_DIE);
pSprite->SetNumFrames(14);
pSprite->SetPosition(iXPos, 0);

// Calculate the velocity so that it is aimed at one of the cities
int iXVel, iYVel = (rand() % 4) + 3;
switch(rand() % 4)
{
case 0:
iXVel = (iYVel * (56 - (iXPos + 50))) / 400;
break;
case 1:
iXVel = (iYVel * (240 - (iXPos + 50))) / 400;
break;
case 2:
iXVel = (iYVel * (360 - (iXPos + 50))) / 400;
break;
case 3:
iXVel = (iYVel * (546 - (iXPos + 50))) / 400;
break;
}
pSprite->SetVelocity(iXVel, iYVel);

// Add the meteor sprite
_pGame->AddSprite(pSprite);
}[/code]

Código fuente


Este tema ha sido archivado. Esto significa que no puedes responder en este tema.
publicidad
publicidad