game loop problem

Started by
6 comments, last by jojo1704 13 years, 2 months ago
hi
so it's my first 2d game
it's simple tank which can move and fire bullets

my problem is with my game loop
my source book like most of c++4game books is using vs2005 and i'm using vs2008

as u know there are some changes in win32api

so the tank moves fine but when im shooting bullets they move a little and just stop until i press another key and then they start to move again
i know problem is my game loop
which instead of running the game is waiting for new messages

my book says is should change the game loop like this



int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow);
{
return run();
}


int run(){
MSG msg;
ZeroMemory(&msg,sizeof(MSG));

while (msg.message !=WM_QUIT)
{
if(PeekMessage(&msg , 0 ,0,0, PM_REMOVE)){

TranslateMessage(&msg);
DispatchMessage(&msg);

}
else
{

game codes
}
}


but my my vs2008 is using int APIENTRY _tWinMain insted of int WINAPI WinMain
which i quite different and seems to have game loop in it's scoop so i didn't know where to put run() function
and when im using it i get a error :The variable 'msg' is being used without being initialized.
so i'm using vs default msg loop

my code now looks like this


int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{

UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);

MSG msg;
HACCEL hAccelTable;

// Initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_DUBLE_BUFFERING, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);

if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}

hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_DUBLE_BUFFERING));

// Main message loop:


while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

else {
//gamecodes
}}



which is works fine when im pressing keys and thank moves
but it doesn't seems to update the game without receiving new msg so my bullets would stop in the middle of screen until next KEY_DOWN

here is complete code
I'd really appreciate if someone could look into it


#include "stdafx.h"
#include "resource.h"
#include "duble_buffering.h"
#include "backbuffer.h"
#include "vec2.h"
#include <string>
#include <list>
#include <mmsystem.h>
using namespace std;

#define MAX_LOADSTRING 100

// Global Variables:
HINSTANCE hInst; // current instance
TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
backbuffer* bb =0;
const int cw=800;
const int ch=600;
const POINT cc={cw/2 , ch/2};
RECT maprect = {0,0,800,600};
vec2 tankpos(400.0f , 300.0f);
HPEN gunpen;
vec2 gundirection(0.0f ,-120.0f);
list<vec2> bulletlist;

float lasttime = (float) timeGetTime ();

// Forward declarations of functions included in this code module:
int RUN();
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{

UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);

// TODO: Place code here.
MSG msg;
HACCEL hAccelTable;

// Initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_DUBLE_BUFFERING, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);

// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}

hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_DUBLE_BUFFERING));




while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

float curtime = (float) timeGetTime ();

float deltatime = (curtime-lasttime)*0.001f;
HDC bbdc = bb->getdc();
HBRUSH oldbrush = (HBRUSH) SelectObject(bbdc ,GetStockObject( BLACK_BRUSH));
Rectangle(bbdc ,0,0,800,600);

SelectObject(bbdc , GetStockObject(DKGRAY_BRUSH));
Rectangle(bbdc,(int)tankpos.x-50 ,
(int)tankpos.y-75 ,
(int)tankpos.x+50 ,
(int)tankpos.y+75 );


SelectObject(bbdc , GetStockObject(DKGRAY_BRUSH));
Ellipse(bbdc,(int)tankpos.x-40 ,
(int)tankpos.y-40 ,
(int)tankpos.x+40 ,
(int)tankpos.y+40);

HPEN oldpen = (HPEN)SelectObject(bbdc , gunpen);
MoveToEx(bbdc , (int)tankpos.x ,(int) tankpos.y,0);
LineTo(bbdc , (int)(tankpos.x+gundirection.x) ,
(int) (tankpos.y+gundirection.y));

SelectObject(bbdc , GetStockObject(WHITE_BRUSH));
SelectObject(bbdc , oldpen);
vec2 bulletvel = gundirection + 0.1f;
list<vec2>::iterator i = bulletlist.begin();

while(i != bulletlist.end()){

//vec2 p = *i * deltatime;
//it's in my book but when im using it i couldn't see the bullet at all


vec2 p = *i;
*i += bulletvel ;

Ellipse(bbdc ,
p.x-4,
p.y-4,
p.x+4,
p.y+4);
++i;

}

SelectObject(bbdc , oldbrush);
bb->peresent();
float lasttime = curtime;
Sleep(20);

}

return (int) msg.wParam;
}

ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;

wcex.cbSize = sizeof(WNDCLASSEX);

wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_DUBLE_BUFFERING));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_DUBLE_BUFFERING);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

return RegisterClassEx(&wcex);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;

hInst = hInstance;
// Store instance handle in our global variable

hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, cw, ch, NULL, NULL, hInstance, NULL);

if (!hWnd)
{
return FALSE;
}

ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);

return TRUE;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
LOGPEN lp;
switch (message)
{
case WM_CREATE:

lp.lopnColor=RGB(150,150,150);
lp.lopnStyle=PS_SOLID;
lp.lopnWidth.x=10;
lp.lopnWidth.y=10;
gunpen = CreatePenIndirect(&lp);

bb = new backbuffer(hWnd , cw , ch );


break;

case WM_KEYDOWN:

switch(wParam){

case 'A' :

tankpos.x -=5.0f;

break;

case 'D' :

tankpos.x +=5.0f;
break;


case 'W' :

tankpos.y -=5.0f;
break;

case 'S':

tankpos.y +=5.0f;
break;

case 'Q' :
gundirection.rotate(-0.1f);
break;

case 'E':
gundirection.rotate (0.1f);
break;
case VK_SPACE:
bulletlist.push_back(tankpos+gundirection);
break;

}
break;

case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
DeleteObject(gunpen);
delete bb;
PostQuitMessage(0);

break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;

case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}





here is my vec2 class :


#include <cmath>

class vec2
{
public:
vec2(void);
vec2(float x0 , float y0);

float x;
float y;


vec2 operator +(vec2& rhs);
vec2 operator +(float a);
vec2& operator +=(vec2& rhs);
vec2 operator *(float s);
vec2& rotate(float r);
~vec2(void);
};

/////////////////////////

#include "StdAfx.h"
#include "vec2.h"
vec2::vec2(){
}

vec2::vec2(float x0,float y0)
{
x = x0 ;
y = y0;
}

vec2 vec2::operator *(float s){
vec2 m;

m.x = x*s;
m.y=y*s;
return m;

}

vec2 vec2::operator +(vec2& rhs){
vec2 add;
add.x = x+rhs.x;
add.y = y+rhs.y;
return add;

}


vec2& vec2::operator +=(vec2& rhs){

x = x+rhs.x;
y = y+rhs.y;
return *this;

}


vec2 vec2::operator +(float a){
vec2 mm;
mm.x=x +a;
mm.y=y +a;

return mm;
}

vec2& vec2::rotate(float r){
x = x*cosf(r) - y* sinf(r);
y = y*cosf(r) + x * sinf(r);
return *this;

}

vec2::~vec2(void)
{
}
Advertisement
You stated the problem yourself! In a game you need to have things updated constantly even if you are not receiving messages. The reason why is that the bullet is only being updated when you receive a message i.e a keypress.

So if you have something like


while (keepGameRunning == true) {
if (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
//gamecodes
}
}
}



That way the game is always updated but messages are only handled when necessary. Thats only some pseudo code but it should work.
You need to use PeekMessage instead of GetMessage, as GetMessage will block and wait when there are no messages.

while(loop) {
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else {
No messages, handle a game frame
}
}
That just shows how much of the Win32 API I've forgotten wink.gif
thanx m8 that did it !

there is another problem
in my source book there is way to find out if the bullet is out of the screen so i could ereas it from my list
bullet are 2d vector so it goes like that

//get point form
//i is a iterator to my buulet list
POINT P = *I ;
if(!PtInRect(&screenmap , p ){

i=buuletlist.erase(i)

........


but in vs 2008 i GET :Error 1 error C2440: 'initializing' : cannot convert from 'vec2' to 'POINT'

is there a way to convert 2d vector to point ?
i could use my vector.x to locate the bullet but PtInRect wouldn't accept it
Looks to me like you have a list of vec2's and are trying to assign its value to a POINT in this line

POINT P = *I ;

Option A: Write a function that will determine whether the point is on the screen (not hard) and make it accept a vec2
Option B: Declare P as vec2 instead of POINT. ( Which will probably mess up the next line)

Option C: Declare your list as POINTS instead of vec2s.

Option D: Simply declare P as is. Then assign P.x to *i.x and P.y to *i.y
Just to tweak your game loop a bit to avoid infrequent problems, you should use while( PeekMessage(...)) instead of if. Somethig like:

while( loop )
{
while(PeekMessage(...))
{
if( msg.message==WM_QUIT)
{
// stop your app
}
TranslateMessage(...);
DispatchMessage(...);
}
// game related stuff
}

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Hey check this link for some more details on your loop problem. The main points were mentioned in this thread, but just for the understanding: http://www.directxtutorial.com/Tutorial11/A-A/AA5.aspx

This topic is closed to new replies.

Advertisement