Hi
I have made a simple Pong game using strictly Win32 C++ functions. But I have run into some problems.
Problems:
1. The sprite for the ball/pong circle should be a perfect white circle but its being draw irregularly (as an irregular ellipse). And my rectangles for my player & computer(bats?) are drawn slightly smaller than they should be drawn. I think there something wrong with my draw function but I dont know what?
2. When the ball/pong moves towards/outside the right & bottom boundary the ball doesn't bounce back like it should but it sticks to the boundary & moves along it. I have looked through my code & I cant seem to figure out why this is happening?
Does anyone know of any Pong tutorials that are for Win32, not SDL etc?
Thanks for reading my post :)
Pong Application http://www.mediafire.com/?eghzmdhej3m
Application code:
#include <windows.h>
#include <stdio.h>
#define IDS_MOVETIME 1
#define IDS_DIRTIME 2
#define IDS_PONGMOVE 3
static HINSTANCE gInstance;
class OBJECT {
public:
HRGN rgn;
HBRUSH hb;
int x;
int y;
int width;
int height;
OBJECT();
bool moveTo(HWND hwnd, RECT &clientR, int n, bool &redraw);
void drawSprite(HDC hdc);
void setStats(int X, int Y, int w, int h, COLORREF fillColour);
};
class BALL {
public:
HRGN rgn;
HBRUSH hb;
int x;
int y;
int width;
int height;
int hSpeed; // horizontal speed
int vSpeed; // vertical speed
BALL();
bool moveTo(HWND hwnd, RECT &clientR, bool &redraw);
bool checkObjectCollision(OBJECT &o);
void drawSprite(HDC hdc);
void setStats(int X, int Y, int w, int h, COLORREF fillColour);
};
OBJECT player;
OBJECT computer;
BALL pong;
bool gameStart = true;
bool redrawPlayer = true;
bool redrawComp = true;
bool redrawPong = true;
int vSpeed = 2;
UINT movementTimer;
UINT directionTimer;
UINT pongMoveTimer;
RECT clientR;
// Functions List //
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void drawBorder(HDC hdc);
void drawObjects(HDC hdc);
int WINAPI WinMain(HINSTANCE gInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
//Step 1: Registering the Window Class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = gInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+3);
wc.lpszMenuName = NULL;
wc.lpszClassName = "My Class";
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
// if registration of main class fails
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Step 2: Creating the Window
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
"My Class",
"Pong",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,500,500,
NULL, NULL, gInstance, NULL);
if(hwnd == NULL)
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Step 3: The Message Loop
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
{
movementTimer = SetTimer(hwnd,IDS_MOVETIME,50,NULL);
directionTimer = SetTimer(hwnd,IDS_DIRTIME,12000,NULL);
pongMoveTimer = SetTimer(hwnd,IDS_PONGMOVE,50,NULL);
GetClientRect(hwnd,&clientR);
player.setStats(10,200,50,100,RGB(255,255,255));
computer.setStats(clientR.right-60,15,50,100,RGB(255,0,0));
pong.setStats(clientR.right-100,40,20,20,RGB(255,255,255));
}
break;
case WM_SIZE:
{
GetClientRect(hwnd,&clientR); // update clientR
computer.x = clientR.right-(computer.width+10); // update computer.x position
gameStart = true; // update White Rectangular Border
redrawPlayer = true;
redrawComp = true;
redrawPong = true;
InvalidateRect(hwnd,NULL,true);
// It is unecessary to update player.x value as it doesn't change
}
break;
case WM_KEYDOWN:
{
switch(LOWORD(wParam)) {
case VK_UP:
{
player.moveTo(hwnd,clientR,-5,redrawPlayer);
}
break;
case VK_DOWN:
{
player.moveTo(hwnd,clientR,5,redrawPlayer);
}
break;
default:
break;
}
}
break;
case WM_TIMER:
{
switch(LOWORD(wParam)) {
case IDS_PONGMOVE:
{ // Move ball/pong & collision check
pong.moveTo(hwnd,clientR,redrawPong);
}
break;
case IDS_MOVETIME:
{ // Move in direction at vSpeed
computer.moveTo(hwnd,clientR,vSpeed,redrawComp);
}
break;
case IDS_DIRTIME:
{ // change computer pong objects' direction
if (vSpeed==2) {
vSpeed = -2; // direction = up
}
else vSpeed = 2; // direction = down
}
break;
default:
break;
}
}
break;
case WM_PAINT:
{
HDC hdc;
PAINTSTRUCT ps;
hdc = BeginPaint(hwnd,&ps);
if (gameStart) {
drawBorder(hdc);
gameStart = false;
}
drawObjects(hdc);
EndPaint(hwnd,&ps);
}
break;
case WM_CLOSE:
DeleteObject(player.hb);
DeleteObject(computer.hb);
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
// Main & Drawing functions
void drawBorder(HDC hdc)
{
// Post: Draw white rectangular border for pong ball to bounce off
HPEN hP = CreatePen(PS_SOLID,2,WHITE_PEN);
SelectObject(hdc,hP);
SetROP2(hdc,R2_NOT);
SelectObject(hdc,GetStockObject(NULL_BRUSH)); // make inside of rectangle transparent DO I NEED TO DeleteObect this??
Rectangle(hdc,3,3,clientR.right-3,clientR.bottom-3);
DeleteObject(hP);
}
void drawObjects(HDC hdc)
{
// Post: Draw player, computer &/or pong when necessary
/*
bool *drawObject[3] = {&redrawPlayer,&redrawComp,&redrawPong);
for (int i=0; i<3; i++) {
if (&drawObject == true) {
}
*/
if (redrawPlayer) {
player.drawSprite(hdc);
redrawPlayer = false;
}
if (redrawComp) {
computer.drawSprite(hdc);
redrawComp = false;
}
if (redrawPong) {
pong.drawSprite(hdc);
redrawPong = false;
}
}
// Class member functions
OBJECT::OBJECT()
{
// Constructor
x = 0;
y = 0;
width = 0;
height = 0;
rgn = NULL;
hb = NULL;
}
bool OBJECT::moveTo(HWND hwnd, RECT &clientR, int n, bool &redraw)
{
// Post: Moves object up or down according to n & updates objects HRGN
// make sure we dont move outside the client window
if ((y+n)>=5 && (y+height+n)<=(clientR.bottom-5)) {
y += n;
redraw = true;
InvalidateRgn(hwnd,rgn,true);
//rgn = CreateRectRgn(x,y,(x+width),(y+height));
return true;
}
else return false;
}
void OBJECT::drawSprite(HDC hdc)
{
// Post: draw sprite
rgn = CreateRectRgn(x,y,(x+width),(y+height));
FillRgn(hdc,rgn,hb);
}
void OBJECT::setStats(int X, int Y, int w, int h, COLORREF fillColour)
{
// Post: Set structures variables
x = X;
y = Y;
width = w;
height = h;
rgn = CreateRectRgn(x,y,x+width,y+height);
hb = CreateSolidBrush(fillColour);
}
BALL::BALL()
{
// Constructor
x = 0;
y = 0;
width = 0;
height = 0;
rgn = NULL;
hb = NULL;
hSpeed = -2;
vSpeed = -2;
}
bool BALL::moveTo(HWND hwnd, RECT &clientR, bool &redraw)
{
// Post: Moves ball/pong up or down according to n & perform collision checking
x += hSpeed;
y += vSpeed;
// if collision with player or computer
if (checkObjectCollision(player) || checkObjectCollision(computer)) {
//x = x-hSpeed;
//y = y-vSpeed;
hSpeed = -hSpeed;
vSpeed = -vSpeed;
return false;
}
// Check for collisions with rectangular barrier
if (x < 6) {
x = 6;
hSpeed = -hSpeed;
}
if (x+width > clientR.right-6) { // this code is not stopping the ball from moving outside the rectangle
x = clientR.right-6;
hSpeed = -hSpeed;
}
if (y < 6) {
y = 6;
vSpeed = -vSpeed;
}
if (y+height > clientR.bottom-6) { // this code is not stopping the ball from moving outside the rectangle
y = clientR.bottom-7;
vSpeed = -vSpeed;
}
redraw = true;
InvalidateRgn(hwnd,rgn,true);
}
bool BALL::checkObjectCollision(OBJECT &o)
{
// Post: Checks for a collision between ball/pong & OBJECT
bool xCollision = false;
bool yCollision = false;
// find left most object
if (x <= o.x) {
// COLLISION, if horizontal distance between this & other object is <= width
if (abs(x-o.x) <= width) { xCollision = true; }
}
// other object = left most object
else if (abs(x-o.x) <= o.width) { xCollision = true; }
// find upper most object
if (y <= o.y) {
// COLLISION, if vertical distance between this & other object is <= height
if (abs(y-o.y) <= height) { yCollision = true; }
}
// other object = upper most object
else if (abs(y-o.y) <= o.height) { yCollision = true; }
return (xCollision==true && yCollision==true);
}
void BALL::drawSprite(HDC hdc)
{
// Post: draw sprite
rgn = CreateEllipticRgn(x,y,(x+width),(y+height));
FillRgn(hdc,rgn,hb);
}
void BALL::setStats(int X, int Y, int w, int h, COLORREF fillColour)
{
// Post: Set structures variables
x = X;
y = Y;
width = w;
height = h;
rgn = CreateEllipticRgn(x,y,x+width,y+height);
hb = CreateSolidBrush(fillColour);
}
Ball draw function & ball collision functions
// ball draw function
void BALL::drawSprite(HDC hdc)
{
// Post: draw sprite
rgn = CreateEllipticRgn(x,y,(x+width),(y+height));
FillRgn(hdc,rgn,hb);
}
// ball collision functions
bool BALL::moveTo(HWND hwnd, RECT &clientR, bool &redraw)
{
// Post: Moves ball/pong up or down according to n & perform collision checking
x += hSpeed;
y += vSpeed;
// if collision with player or computer
if (checkObjectCollision(player) || checkObjectCollision(computer)) {
//x = x-hSpeed;
//y = y-vSpeed;
hSpeed = -hSpeed;
vSpeed = -vSpeed;
return false;
}
// Check for collisions with rectangular barrier
if (x < 6) {
x = 6;
hSpeed = -hSpeed;
}
if (x+width > clientR.right-6) { // this code is not stopping the ball from moving outside the rectangle
x = clientR.right-6;
hSpeed = -hSpeed;
}
if (y < 6) {
y = 6;
vSpeed = -vSpeed;
}
if (y+height > clientR.bottom-6) { // this code is not stopping the ball from moving outside the rectangle
y = clientR.bottom-7;
vSpeed = -vSpeed;
}
redraw = true;
InvalidateRgn(hwnd,rgn,true);
}
bool BALL::checkObjectCollision(OBJECT &o)
{
// Post: Checks for a collision between ball/pong & OBJECT
bool xCollision = false;
bool yCollision = false;
// find left most object
if (x <= o.x) {
// COLLISION, if horizontal distance between this & other object is <= width
if (abs(x-o.x) <= width) { xCollision = true; }
}
// other object = left most object
else if (abs(x-o.x) <= o.width) { xCollision = true; }
// find upper most object
if (y <= o.y) {
// COLLISION, if vertical distance between this & other object is <= height
if (abs(y-o.y) <= height) { yCollision = true; }
}
// other object = upper most object
else if (abs(y-o.y) <= o.height) { yCollision = true; }
return (xCollision==true && yCollision==true);
}