Jump to content

Featured Replies

Posted

Breakout. Made using no tutorials. I'll add in double buffering as soon as I figure out what the hell it is.

 

main.cpp

#include <windows.h>
#include <ctime>
#include "definitions.h"
#include "objects.h"

void CleanUp()
{
int count;
for (count = 0; count < MAX_BLOCKS; count++)
	delete Blocks[count];
KillTimer(MainWindow, 1);
delete Paddle;
delete Ball;
}

void DisplayInterface(HDC hdcWindow)
{
HBITMAP hBitmap;
HDC hdcInterface = CreateCompatibleDC(hdcWindow);
hBitmap = (HBITMAP)LoadImage(NULL,"data\\interface.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
SelectObject(hdcInterface,hBitmap);
BitBlt(hdcWindow,0,0,HWND_WIDTH,HWND_HEIGHT,hdcInterface,0,0,SRCCOPY);
DeleteObject(hBitmap);
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
int count;
   switch(msg)
   {
	case WM_CREATE:
		for (count = 0; count < MAX_BLOCKS; count++)
			Blocks[count] = NULL;
		Paddle = new PaddleObject;
		Ball = new BallObject;
		Paddle->x = 200;
		SetTimer(hwnd, 1, STEP_INTERVAL, NULL);
		CurrentLevel = 0;
		ScoreCount = 0;
	break;
       case WM_CLOSE:
           DestroyWindow(hwnd);
       break;
       case WM_TIMER:{
		HDC hdcWindow = GetDC(hwnd);
			hdcPointer = &hdcWindow;
			Ball->EraseBall(hdcWindow);
			Ball->HandleCollisions(Ball->CheckCollisions());
			Ball->MoveBall();
			Ball->DisplayBall(hdcWindow);
			Paddle->ErasePaddle(hdcWindow);
			if (GetAsyncKeyState(VK_LEFT))
				Paddle->MovePaddle(-1*Paddle->speed);
			if (GetAsyncKeyState(VK_RIGHT))
				Paddle->MovePaddle(Paddle->speed);
			Paddle->DisplayPaddle(hdcWindow);
			for (count = 0; count < MAX_BLOCKS; count++){
				if (Blocks[count] != NULL)
					Blocks[count]->DisplayBlock(hdcWindow);
			}
		ReleaseDC(hwnd,hdcWindow);
	}
	break;
       case WM_DESTROY:
           PostQuitMessage(0);
       break;
       case WM_PAINT:{
       	PAINTSTRUCT ps;
           HDC hdcWindow;
           hdcWindow = BeginPaint(hwnd, &ps);
		DisplayInterface(hdcWindow);
           EndPaint(hwnd, &ps);
           DeleteDC(hdcWindow);
	}
       break;
	case WM_KEYDOWN:
		switch(wParam){
			case VK_ESCAPE:
				DestroyWindow(MainWindow);
				return 0;
			break;
		}
	break;
       default:
           return DefWindowProc(hwnd, msg, wParam, lParam);
   }
   return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
   LPSTR lpCmdLine, int nCmdShow)
{
   WNDCLASSEX wc;
   MSG msg;
   
   srand(time(NULL));
   
   BackgroundBrush = CreateSolidBrush(RGB(0,64,64));
   
   wc.cbSize        = sizeof(WNDCLASSEX);
   wc.style         = 0;
   wc.lpfnWndProc   = WndProc;
   wc.cbClsExtra    = 0;
   wc.cbWndExtra    = 0;
   wc.hInstance     = hInstance;
   wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
   wc.lpszMenuName  = NULL;
   wc.lpszClassName = CLASS_NAME;
   wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

   if(!RegisterClassEx(&wc))
   {
       MessageBox(NULL, "Window Registration Failed!", "Error!",
           MB_ICONEXCLAMATION | MB_OK);
       return 0;
   }

   MainWindow = CreateWindowEx(
       WS_EX_CLIENTEDGE,
       CLASS_NAME,
       APP_NAME,
       WS_CAPTION | WS_SYSMENU | WS_OVERLAPPED,
       CW_USEDEFAULT, CW_USEDEFAULT, HWND_WIDTH, HWND_HEIGHT,
       NULL, NULL, hInstance, NULL);

   ShowWindow(MainWindow, nCmdShow);
   UpdateWindow(MainWindow);

GameIsOver = false;

while(GetMessage(&msg, NULL, 0, 0))
{
       TranslateMessage(&msg);
       DispatchMessage(&msg);
       if (GameIsOver)
       	break;
   }
   CleanUp();
   MessageBox(MainWindow,"Game Over.",APP_NAME,MB_OK);
   return msg.wParam;
}

  • Author

objects.h

#ifndef OBJECTS_H
#define OBJECTS_H

HDC* hdcPointer;
INT ScoreCount;
INT CurrentLevel;
BOOL GameIsOver;
HBRUSH BackgroundBrush;
HWND MainWindow;

//-----------------------------------------------------------------
//-----------------------------------------------------------------
//-----------------------------------------------------------------

class BitmapObject{
public:
	int x;
	int y;
	int width;
	int height;
	int id;
	operator HDC();
	BitmapObject();
	~BitmapObject();
	void DisplayObject(HDC hdcBuffer);
	bool GetImage(const char* path, int xOrigin, int yOrigin);
	bool DestroyObject();
};

class BallObject : public BitmapObject
{
public:
	int xvector;
	int yvector;
	HBITMAP hBitmap;
	HBITMAP hMask;
	HDC hdcBall;
	BallObject();
	~BallObject();
	int CheckCollisions();
	bool HandleCollisions(int collision);
	void DestroyBall();
	void DisplayBall(HDC hdcBuffer);
	void MoveBall();
	void EraseBall(HDC hdcBuffer);
};

BallObject* Ball;

class BlockObject : public BitmapObject
{
public:
	int health;
	COLORREF colour;
	BlockObject(int newid);
	~BlockObject();
	void DamageBlock(int factor);
	void DisplayBlock(HDC hdcBuffer);
	void WipeBlock(HDC hdcBuffer);
	void DestroyBlock();
};

BlockObject* Blocks[MAX_BLOCKS];

class PaddleObject : public BitmapObject
{
public:
	int speed;
	int deltax;
	int xvector;
	COLORREF colour;
	HDC hdcPaddle;
	PaddleObject();
	~PaddleObject();
	void DestroyPaddle();
	void DisplayPaddle(HDC hdcBuffer);
	void MovePaddle(int xvector);
	void ErasePaddle(HDC hdcBuffer);
};

PaddleObject* Paddle;

//-----------------------------------------------------------------
//-----------------------------------------------------------------
//-----------------------------------------------------------------

BitmapObject::BitmapObject()
{
id = -1;
x = 0;
y = 0;
width = 0;
height = 0;
}

BitmapObject::~BitmapObject()
{
DestroyObject();
}

bool BitmapObject::DestroyObject()
{
return true;
}

//-----------------------------------------------------------------
//-----------------------------------------------------------------
//-----------------------------------------------------------------

BlockObject::BlockObject(int newid)
{
id = newid;	
health = DEFAULT_BRICK_HEALTH;
width = DEFAULT_BRICK_WIDTH;
height = DEFAULT_BRICK_HEIGHT;
colour = RGB(rand()%256,rand()%256,rand()%256);
}

BlockObject::~BlockObject()
{
DestroyBlock();
}

void BlockObject::DestroyBlock()
{
WipeBlock(*hdcPointer);
DestroyObject();
Blocks[id] = NULL;
}

void BlockObject::WipeBlock(HDC hdcBuffer)
{
RECT rc;
rc.left = x;
rc.top = y;
rc.right = x + width+1;
rc.bottom = y + height+1;
FillRect(hdcBuffer,&rc,BackgroundBrush);
}

void BlockObject::DamageBlock(int factor)
{
health -= factor;
if (health <= 0)
	DestroyBlock();
else
	colour = RGB(rand()%256,rand()%256,rand()%256);
}

void BlockObject::DisplayBlock(HDC hdcBuffer)
{
int count;
int count2;
//Outline
for (count = 0; count <= DEFAULT_BRICK_WIDTH; count++){
	for (count2 = 0; count2 <= DEFAULT_BRICK_HEIGHT; count2++)
		SetPixel(hdcBuffer,x+count,y+count2,0);
}
//Colour
for (count = DEFAULT_BRICK_THICKNESS-1; count <= DEFAULT_BRICK_WIDTH-DEFAULT_BRICK_THICKNESS; count++){
	for (count2 = DEFAULT_BRICK_THICKNESS; count2 <= DEFAULT_BRICK_HEIGHT-DEFAULT_BRICK_THICKNESS+1; count2++)
		SetPixel(hdcBuffer,x+count,y+count2,colour);
}
}

//-----------------------------------------------------------------
//-----------------------------------------------------------------
//-----------------------------------------------------------------

BallObject::BallObject()
{
x = 250;
y = 200;
xvector = -1 * DEFAULT_BALL_SPEED;
yvector = -1 * DEFAULT_BALL_SPEED;
hdcBall = NULL;
hBitmap = NULL;
hMask = NULL;
width = 16;
height = 16;
}

BallObject::~BallObject()
{
DestroyBall();
}

void BallObject::DestroyBall()
{
DestroyObject();
DeleteDC(hdcBall);
DeleteObject(hBitmap);
DeleteObject(hMask);
}

void BallObject::MoveBall()
{
x += xvector;
y += yvector;
}

void BallObject::EraseBall(HDC hdcBuffer)
{
RECT rc;
rc.top = y;
rc.bottom = y+height;
rc.left = x;
rc.right = x+width;
FillRect(hdcBuffer,&rc,BackgroundBrush);
}

int BallObject::CheckCollisions()
{
int count;
if ((x + xvector) > SCREEN_EDGE_RIGHT)
	return COLLISION_SCREEN_RIGHT;
else if ((x + xvector) < SCREEN_EDGE_LEFT)
	return COLLISION_SCREEN_LEFT;
else if ((y + yvector) > SCREEN_EDGE_BOTTOM)
	return COLLISION_SCREEN_BOTTOM;
else if ((y + yvector) < SCREEN_EDGE_TOP)
	return COLLISION_SCREEN_TOP;
else if ( ( y + yvector >= Paddle->y - 13 ) && ( ( x + xvector >= Paddle->x + (Paddle->width/4) ) && ( x + xvector <= Paddle->x+Paddle->width - (Paddle->width/4) ) ) )
	return COLLISION_PADDLE_CENTER;
else if ( ( y + yvector >= Paddle->y - 13 ) && ( ( x + xvector <= Paddle->x + (Paddle->width/4) ) && ( x + xvector >= Paddle->x ) ) )
	return COLLISION_PADDLE_LEFT;
else if ( ( y + yvector >= Paddle->y - 13 ) && ( ( x + xvector <= Paddle->x +Paddle->width ) && ( x + xvector >= Paddle->x+Paddle->width - (Paddle->width/4) ) ) )
	return COLLISION_PADDLE_RIGHT;
else{
	for (count = 0; count < MAX_BLOCKS; count++){
		if (Blocks[count] == NULL)
			break;
		else{
			if ( ( ( y + yvector >= Blocks[count]->y-6 ) && ( y + yvector <= Blocks[count]->y+Blocks[count]->height+3 ) ) && ( ( x + xvector <= Blocks[count]->x+Blocks[count]->width ) && (x + xvector >= Blocks[count]->x ) ) )
				return count+20;
		}
	}
}
return COLLISION_NO_COLLISION;
}

bool BallObject::HandleCollisions(int collision)
{
if (collision == COLLISION_NO_COLLISION)
	return false;
else{
	if (collision == COLLISION_SCREEN_RIGHT || collision == COLLISION_SCREEN_LEFT)
		xvector *= -1;
	else if (collision == COLLISION_SCREEN_TOP)
		yvector *= -1;
	else if (collision == COLLISION_SCREEN_BOTTOM)
		GameIsOver = true;
	else if (collision == COLLISION_PADDLE_CENTER)
		yvector *= -1;
	else if (collision == COLLISION_PADDLE_RIGHT){
		if (xvector == DEFAULT_BALL_SPEED*-2)
			xvector = DEFAULT_BALL_SPEED*-1;
		else if (xvector == DEFAULT_BALL_SPEED*-1)
			xvector = DEFAULT_BALL_SPEED;
		else if (xvector == DEFAULT_BALL_SPEED)
			xvector = DEFAULT_BALL_SPEED*2;
		else if (xvector == DEFAULT_BALL_SPEED*2)
			xvector = DEFAULT_BALL_SPEED*2;
		yvector *= -1;
	}
	else if (collision == COLLISION_PADDLE_LEFT){
		if (xvector == DEFAULT_BALL_SPEED*-2)
			xvector = DEFAULT_BALL_SPEED*-2;
		else if (xvector == DEFAULT_BALL_SPEED*-1)
			xvector = DEFAULT_BALL_SPEED*-2;
		else if (xvector == DEFAULT_BALL_SPEED)
			xvector = DEFAULT_BALL_SPEED*-1;
		else if (xvector == DEFAULT_BALL_SPEED*2)
			xvector = DEFAULT_BALL_SPEED;
		yvector *= -1;
	}
	else if (collision >= 20){
		Blocks[collision-20]->DamageBlock(1);
		yvector *= -1;
	}
	return true;
}
return false;
}

void BallObject::DisplayBall(HDC hdcBuffer)
{
hdcBall = CreateCompatibleDC(hdcBuffer);
hBitmap = (HBITMAP)LoadImage(NULL,"data\\ball.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
hMask = (HBITMAP)LoadImage(NULL,"data\\ballmask.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
SelectObject(hdcBall,hMask);
BitBlt(hdcBuffer,x,y,width,height,hdcBall,0,0,SRCAND);
SelectObject(hdcBall,hBitmap);
BitBlt(hdcBuffer,x,y,width,height,hdcBall,0,0,SRCPAINT);
DeleteDC(hdcBall);
DeleteObject(hBitmap);
}

//-----------------------------------------------------------------
//-----------------------------------------------------------------
//-----------------------------------------------------------------

PaddleObject::PaddleObject()
{
speed = DEFAULT_PADDLE_SPEED;
width = DEFAULT_PADDLE_WIDTH;
colour = RGB(255,255,0);
deltax = 0;
height = DEFAULT_PADDLE_HEIGHT;
y = SCREEN_EDGE_BOTTOM-11;
hdcPaddle = NULL;
}

PaddleObject::~PaddleObject()
{
DestroyPaddle();
}

void PaddleObject::DestroyPaddle()
{
DestroyObject();
DeleteDC(hdcPaddle);
}

void PaddleObject::DisplayPaddle(HDC hdcBuffer)
{
int count;
int count2;
for (count = 1; count < width; count++){
	SetPixel(hdcBuffer,count+x,y,RGB(0,0,0));
	SetPixel(hdcBuffer,count+x,height+y,RGB(0,0,0));
	SetPixel(hdcBuffer,count+x,1+y,RGB(0,0,0));
	SetPixel(hdcBuffer,count+x,y+(height-1),RGB(0,0,0));
}
for (count = 1; count < height-1; count++){
	SetPixel(hdcBuffer,x,count+y,RGB(0,0,0));
	SetPixel(hdcBuffer,x+1,count+y,RGB(0,0,0));
	SetPixel(hdcBuffer,x+width,count+y,RGB(0,0,0));
	SetPixel(hdcBuffer,x+(width-1),count+y,RGB(0,0,0));
}
for (count = 2; count < width-2; count++){
	for (count2 = 2; count2 < height-2; count2++)
		SetPixel(hdcBuffer,count+x,count2+y,colour);
}
}

void PaddleObject::ErasePaddle(HDC hdcBuffer)
{
RECT rc;
rc.top = y;
rc.bottom = y+height+1;
rc.left = x;
rc.right = x+width+1;
FillRect(hdcBuffer,&rc,BackgroundBrush);
}

void PaddleObject::MovePaddle(int xvector)
{
if ( (x + xvector < SCREEN_EDGE_RIGHT-42) && (x + xvector > SCREEN_EDGE_LEFT-2) ) {
	x += xvector;
}
}


#endif

  • Author

defnitions.h

#ifndef DEFINITIONS_H
#define DEFINITIONS_H

#define APP_NAME "Breakout"
#define CLASS_NAME "BreakoutClass"
#define MAX_BLOCKS 128
#define HWND_WIDTH 810
#define HWND_HEIGHT 631
#define SCREEN_EDGE_RIGHT 520
#define SCREEN_EDGE_LEFT 20
#define SCREEN_EDGE_BOTTOM 572
#define SCREEN_EDGE_TOP 15
#define COLLISION_NO_COLLISION -1
#define COLLISION_SCREEN_RIGHT 1
#define COLLISION_SCREEN_LEFT 2
#define COLLISION_SCREEN_TOP 3
#define COLLISION_SCREEN_BOTTOM 4
#define COLLISION_BRICK_BOTTOM 5
#define COLLISION_BRICK_LEFT 6
#define COLLISION_BRICK_RIGHT 7
#define COLLISION_BRICK TOP 8
#define COLLISION_PADDLE_LEFT 9
#define COLLISION_PADDLE_RIGHT 10
#define COLLISION_PADDLE_CENTER 11
#define DEFAULT_PADDLE_WIDTH 60
#define DEFAULT_PADDLE_SPEED 4
#define DEFAULT_PADDLE_HEIGHT 15
#define DEFAULT_BALL_SPEED 3
#define DEFAULT_BRICK_WIDTH 80
#define DEFAULT_BRICK_HEIGHT 15
#define DEFAULT_BRICK_THICKNESS 4
#define DEFAULT_BRICK_HEALTH 2
#define STEP_INTERVAL 20

#endif

 

I haven't added in a scoring system, levels or items, yet, but it's all set up for it. I also plan to make a level editor.

 

Now, if anyone knows how I would go about double-buffering this, to get rid of that annoying flickering, I'd like to know :).

Double buffer = draw current "frame" onto a surface stored somewhere in memory, and swap it with the current one (being displayed) at the bottom of your main loop. As I'm sure you know, it prevents the flickering when you draw.

 

Wanna zip the contents of /data and host that too? :)

 

Normally you would do srand(GetTickCount()), since the ticks since the system booted is never the same. I've seen other ways to do it with a time_t, but GetTickCount() is the most common.

 

After you GetDC(), you should SetBkColor(hdc, RGB(255,255,255)) to clear the drawing area, lest this happens: http://xs57.xs.to/pics/05485/bout.png

 

Also, generally you would have your main logic loop like this:

 

while(1) // or for(;;) or w/e you prefer
{
   while(GetMessage(&msg, NULL, 0, 0))
   {
       TranslateMessage(&msg);
       DispatchMessage(&msg);
   }

   if (GameIsOver)
       break;

   ProcessAI();
   DoGameLogic();
   OtherBS();
   etc();

   DrawEntireSceneToBackBuffer();

   // swap back buffer with current (double buffer) here

}

return;

 

instead of relying on the WM_TIMER for redrawing/logic processing. There are more accurate timers to use than that anyways.

 

Lots of little things, but Win32 is complex at first. Keep working at it though!

 

Edit:

 

2 macros that are useful:

 

#define KeyDown(vk_code)    ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KeyUp(vk_code)    ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)

 

Then you can just go:

 

if(KeyDown(VK_ESCAPE)) { /* do stuff */ }

  • Author
Double buffer = draw current "frame" onto a surface stored somewhere in memory, and swap it with the current one (being displayed) at the bottom of your main loop. As I'm sure you know, it prevents the flickering when you draw.

 

Wanna zip the contents of /data and host that too? :)

 

Normally you would do srand(GetTickCount()), since the ticks since the system booted is never the same. I've seen other ways to do it with a time_t, but GetTickCount() is the most common.

 

After you GetDC(), you should SetBkColor(hdc, RGB(255,255,255)) to clear the drawing area, lest this happens: http://xs57.xs.to/pics/05485/bout.png

 

Also, generally you would have your main logic loop like this:

 

while(1) // or for(;;) or w/e you prefer
{
   while(GetMessage(&msg, NULL, 0, 0))
   {
       TranslateMessage(&msg);
       DispatchMessage(&msg);
   }

   if (GameIsOver)
       break;

   ProcessAI();
   DoGameLogic();
   OtherBS();
   etc();

   DrawEntireSceneToBackBuffer();

   // swap back buffer with current (double buffer) here

}

return;

 

instead of relying on the WM_TIMER for redrawing/logic processing. There are more accurate timers to use than that anyways.

 

Lots of little things, but Win32 is complex at first. Keep working at it though!

 

Edit:

 

2 macros that are useful:

 

#define KeyDown(vk_code)    ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KeyUp(vk_code)    ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)

 

Then you can just go:

 

if(KeyDown(VK_ESCAPE)) { /* do stuff */ }

 

Well, the interface is meant to take up the background, it looks fine once you have the data folder. I'll put up the stuff later tongiht, I'm not home at the moment. And I'll try and work those mods in. Thanks asterix :)

  • Author

case WM_TIMER:{
		HDC hdcWindow = GetDC(hwnd);
		HDC hdcBuffer = CreateCompatibleDC(hdcWindow);
			hdcPointer = &hdcBuffer;
			Ball->EraseBall(hdcBuffer);
			Ball->HandleCollisions(Ball->CheckCollisions());
			Ball->MoveBall();
			Ball->DisplayBall(hdcBuffer);
			Paddle->ErasePaddle(hdcBuffer);
			if (GetAsyncKeyState(VK_LEFT))
				Paddle->MovePaddle(-1*Paddle->speed);
			if (GetAsyncKeyState(VK_RIGHT))
				Paddle->MovePaddle(Paddle->speed);
			Paddle->DisplayPaddle(hdcBuffer);
			for (count = 0; count < MAX_BLOCKS; count++){
				if (Blocks[count] != NULL)
					Blocks[count]->DisplayBlock(hdcBuffer);
			}
			BitBlt(hdcWindow,0,0,HWND_WIDTH,HWND_HEIGHT,hdcBuffer,0,0,SRCCOPY);
		DeleteDC(hdcBuffer);
		ReleaseDC(hwnd,hdcWindow);
	}

 

That's my game loop now...

 

Now, as I understand the GDI, that should work. But it doesn't display the paddle or ball... any reason for that?

Not sure what exactly you're trying to accomplish there... looks like you're trying to set it up so you can double buffer. Maybe post a link to the tut/example you're looking at? (It's been a few years myself, I could use a review :P)

 

Also one thing I forgot to mention, at the bottom of the "main loop" I posted, you will need to Sleep() for a few milliseconds to burn time (and so your app doesn't use 100% CPU all the time).

  • Author
Well, I'm not using a tutorial or an example... I just thought that this would work... I don't see why it shouldn't...
Guest
This topic is now closed to further replies.