Ir al contenido

publicidad
publicidad

Foto

Curso MM: 8 Sprites


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

#1

Escrito 11 agosto 2009 - 21:52

Haciendo que las cosas se muevan con sprites

La clase Bitmap se encarga del aspecto físico del objeto. La clase Sprite se encargará de su comportamiento.

Diseñando un Sprite multipropósito

Propiedades de un Sprite que hay que tener en cuenta: posición, velocidad, orden-z (en qué orden se va a dibujar), rectángulo delimitador, acciones del rectángulo, oculto/visible.

La velocidad se usa en cada ciclo para cambiar la posición del sprite. La posición se determina mejor con un rectángulo alrededor del sprite, del que podemos obtener su ancho y alto. El rectángulo delimitador indica los límites en los que se va a mover el sprite. Las acciones del rectángulo delimitador indican qué hacemos cuando se alcanza un límite (stop, wrap, bounce, die) (parar,recortar, rebotar, morir).

Creando la clase sprite (tendremos dos nuevos archivos Sprite.h y Sprite.cpp)

[code:1]class Sprite
{
protected:
// Member Variables
Bitmap* m_pBitmap; //puntero al bitmap.
RECT m_rcPosition; //rectángulo de posición.
POINT m_ptVelocity; //velocidad.
int m_iZOrder; //orden-z.
RECT m_rcBounds; //rectángulo delimitador.
BOUNDSACTION m_baBoundsAction; //acción a realizar al alcanzar un límite.
BOOL m_bHidden; //oculto/visible.

public:
// Constructor(s)/Destructor
//tenemos 3. En uno sólo necesitamos indicar el bitmap. En otro el rectángulo delimitador
//y la acción a llevar a cabo al tocar los límites. En el tercero damos casi toda la información.
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 void Update(); //aplica la velocidad a la posición y calcula las reacciones.
void Draw(HDC hDC);
BOOL IsPointInside(int x, int y); //para ver si se ha pulsado sobre el sprite.

// 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);
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(); };
};[/code]

El nuevo tipo de datos BOUNDSACTION:

[code:1]typedef WORD BOUNDSACTION;
const BOUNDSACTION BA_STOP = 0,
BA_WRAP = 1,
BA_BOUNCE = 2,
BA_DIE = 3;[/code]

Así le decimos a cada sprite cómo debe reaccionar si llega a un límite.

Creando y destruyendo un Sprite


[code:1]Sprite::Sprite(Bitmap* pBitmap)
{
// Initialize the member variables
m_pBitmap = pBitmap;
SetRect(&m_rcPosition, 0, 0, pBitmap->GetWidth(), pBitmap->GetHeight());
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());
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, pBitmap->GetWidth(),
pBitmap->GetHeight());
m_ptVelocity = ptPosition;
m_iZOrder = iZOrder;
CopyRect(&m_rcBounds, &rcBounds);
m_baBoundsAction = baBoundsAction;
m_bHidden = FALSE;
}

Sprite::~Sprite()
{
}[/code]

Actualizando el Sprite

[code:1]void Sprite::Update()
{
// Actualizar la posición. Se le suma la velocidad.
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;

// Comprobar los límites.
// Wrap? Si sale por un lado aparece por el otro.
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? Rebotar.
else if (m_baBoundsAction == BA_BOUNCE)
{
BOOL bBounce = FALSE;
POINT ptNewVelocity = m_ptVelocity;
if (ptNewPosition.x < m_rcBounds.left) //si tocamos el borde izquierdo.
{
bBounce = TRUE; //activamos el rebote.
ptNewPosition.x = m_rcBounds.left; //colocamos el sprite en el borde.
ptNewVelocity.x = -ptNewVelocity.x; //invertimos la velocidad.
}
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); //si hemos activado el rebote
//ponemos la nueva velocidad.
}
// Stop (default). Al tocar un borde se para (por defecto).
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);
}[/code]

Dibujando un Sprite

[code:1]void Sprite::Draw(HDC hDC)
{
// Dibujar el sprite si no está marcado como oculto.
if (m_pBitmap != NULL && !m_bHidden)
m_pBitmap->Draw(hDC, m_rcPosition.left, m_rcPosition.top, TRUE);
}[/code]

Programa de ejemplo: Fore.h , Fore.cpp
En Fore.h podemos ver dos variables que se usan para saber qué bola está siendo arrastrada (un índice a la tabla de sprites) y si hay alguna que esté siendo arrastrada. También hay que mirar las funciones del ratón, que llevan a cabo el trabajo de arrastrar y soltar.

Fore.h
[code:1]//-----------------------------------------------------------------
// Fore Application
// C++ Header - Fore.h
//-----------------------------------------------------------------

#pragma once

//-----------------------------------------------------------------
// Include Files
//-----------------------------------------------------------------
#include
#include "Resource.h"
#include "GameEngine.h"
#include "Bitmap.h"
#include "Sprite.h"

//-----------------------------------------------------------------
// Global Variables
//-----------------------------------------------------------------
HINSTANCE _hInstance;
GameEngine* _pGame;
Bitmap* _pForestBitmap; //el dibujo para el fondo.
Bitmap* _pGolfBallBitmap; //bitmap de una pelota de golf.
Sprite* _pGolfBallSprite[3]; //tabla para guardar 3 sprites.
BOOL _bDragging; //indica si se está arrastrando algo o no.
int _iDragBall; //entero que indica qué bola se está arrastrando.[/code]

Fore.cpp
[code:1]//-----------------------------------------------------------------
// Fore Application
// C++ Source - Fore.cpp
//-----------------------------------------------------------------

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

//-----------------------------------------------------------------
// Game Engine Functions
//-----------------------------------------------------------------
BOOL GameInitialize(HINSTANCE hInstance)
{
// Create the game engine
_pGame = new GameEngine(hInstance, TEXT("Fore"),
TEXT("Fore"), IDI_FORE, IDI_FORE_SM, 600, 400);
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 and load the bitmaps
HDC hDC = GetDC(hWindow);
_pForestBitmap = new Bitmap(hDC, IDB_FOREST, _hInstance);
_pGolfBallBitmap = new Bitmap(hDC, IDB_GOLFBALL, _hInstance);

// Creamos los sprites de las bolas de golf.
RECT rcBounds = { 0, 0, 600, 400 }; //el rectángulo con los límites
//abarca toda la pantalla del juego.
_pGolfBallSprite[0] = new Sprite(_pGolfBallBitmap, rcBounds);
_pGolfBallSprite[1] = new Sprite(_pGolfBallBitmap, rcBounds, BA_WRAP);
_pGolfBallSprite[2] = new Sprite(_pGolfBallBitmap, rcBounds, BA_BOUNCE);
//se crean tres, uno tendrá la acción por defecto de pararse al tocar un límite.
//Otro aparecerá por el otro lado al salir por un borde. Y el tercero rebotará.

_pGolfBallSprite[0]->SetVelocity(2, 1);
_pGolfBallSprite[1]->SetVelocity(3, -2);
_pGolfBallSprite[2]->SetVelocity(7, 4);

// Set the initial drag info
_bDragging = FALSE; //no arrastramos.
_iDragBall = -1; //el índice está fuera de la tabla.
}

void GameEnd()
{
// Cleanup the bitmaps
delete _pForestBitmap;
delete _pGolfBallBitmap;

// Cleanup the sprites
for (int i = 0; i < 3; i++)
delete _pGolfBallSprite[i];

// Cleanup the game engine
delete _pGame;
}

void GameActivate(HWND hWindow)
{
}

void GameDeactivate(HWND hWindow)
{
}

void GamePaint(HDC hDC)
{
// Draw the background forest
_pForestBitmap->Draw(hDC, 0, 0);

// Gracias al motor del juego es fácil dibujar los sprites de las bolas de golf.
for (int i = 0; i < 3; i++)
_pGolfBallSprite[i]->Draw(hDC);
}

void GameCycle()
{
// Actualizar los sprites de las bolas de golf.
for (int i = 0; i < 3; i++)
_pGolfBallSprite[i]->Update();

// Forzar el redibujado de la pantalla.
InvalidateRect(_pGame->GetWindow(), NULL, FALSE);
}

void HandleKeys()
{
}

void MouseButtonDown(int x, int y, BOOL bLeft)
{
// Comprobar si se ha pulsado sobre una bola con el botón izquierdo del ratón pulsado.
// Y también si no estabamos ya arrastrando algo.
if (bLeft && !_bDragging)
{
for (int i = 0; i < 3; i++)
if (_pGolfBallSprite[i]->IsPointInside(x, y))
{
// Capture the mouse
SetCapture(_pGame->GetWindow());

// Set the drag state and the drag ball
_bDragging = TRUE;
_iDragBall = i;

// Simulate a mouse move to get started
MouseMove(x, y);

// Don't check for more balls
break;
}
}
}

void MouseButtonUp(int x, int y, BOOL bLeft)
{
// Release the mouse
ReleaseCapture();

// Stop dragging
_bDragging = FALSE; //dejamos de arrastrar.
}

void MouseMove(int x, int y)
{
if (_bDragging)
{
// Si estamos arrastrando algo movemos el sprite a la posición del cursor del ratón.
_pGolfBallSprite[_iDragBall]->SetPosition(
x - (_pGolfBallBitmap->GetWidth() / 2),
y - (_pGolfBallBitmap->GetHeight() / 2));

// Forzar un redibujado.
InvalidateRect(_pGame->GetWindow(), NULL, FALSE);
}
}

void HandleJoystick(JOYSTATE jsJoystickState)
{
}[/code]

Código fuente


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