# GDI issues again!

This topic is 4761 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Very quickly, thanks for all your help so far. I nearly have my first game written - 'Rectangular monsters from the 600x600 black square planet!!' Hmm... Anyway, I'm trying to draw things in Game_Init etc but it doesn;t work. Drawing only seems to work in the WinMain event loop. Here is my complete code -
// My first game - based on T3D console app

// INCLUDES ///////////////////////////////////////////////
#define WIN32_LEAN_AND_MEAN  // No FC

#include <windows.h>   // include important windows stuff
#include <windowsx.h>
#include <mmsystem.h>
#include <iostream.h> // include important C/C++ stuff
#include <conio.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <math.h>
#include <io.h>
#include <fcntl.h>

// DEFINES ////////////////////////////////////////////////

// defines for windows
#define WINDOW_CLASS_NAME "WINCLASS1"

// MACROS /////////////////////////////////////////////////

#define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEYUP(vk_code)   ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)

// GLOBALS ////////////////////////////////////////////////
HWND      main_window_handle = NULL; // globally track main window
HINSTANCE hinstance_app      = NULL; // globally track hinstance

int winheight				= 600;	// Initial Window Height
int winwidth				= 600;	// Initial Window Width

int score					= 0;	 // Score
int level					= 1;	 // Level
int leftlimit				= 1;	// Left movement limit
int rightlimit				= 550;	// Right movement limit
int toplimit				= 30;	// Top movement limit
int botlimit				= 550;	// Bottom Movement limit

char buffer[80];                     // general printing buffer

// Player vars

int plife					= 50;	 // Life
int  pxul					= 40;	 // X Upper left
int  pyul					= 40;	 // Y Upper left etc...
int  pxbr					= 50;
int  pybr					= 50;

int pprevxul					= 0;	 // Previous positions
int pprevyul					= 0;
int pprevxbr					= 0;
int pprevybr					= 0;

// Enemy vars

int  exul					= 300;	 // X Upper left
int  eyul					= 300;	 // Y Upper left etc...
int  exbr					= 310;
int  eybr					= 310;

int eprevxul					= 0;	 // Previous positions
int eprevyul					= 0;
int eprevxbr					= 0;
int eprevybr					= 0;

// Bullet vars

int pbulletx				= NULL; // Previous Position
int pbullety				= NULL;
int bulletx					= NULL;	// Bullet x co-ord
int bullety					= NULL;	// Bullet y co-ord
int bulletaimx				= NULL; // Bullet aim point x co-ord
int bulletaimy				= NULL; // Bullet aim point y co-ord

int bulletspeed				= 5;	// Speed
int brange					= 100;	// Range
int bdistance				= 0;	// Distance Travelled

// Global

// FUNCTIONS //////////////////////////////////////////////
LRESULT CALLBACK WindowProc(HWND hwnd,
UINT msg,
WPARAM wparam,
LPARAM lparam)
{
// this is the main message handler of the system
PAINTSTRUCT		ps;		// used in WM_PAINT
HDC				hdc;	// handle to a device context
char buffer[80];        // used to print strings

// what is the message
switch(msg)
{
case WM_CREATE:
{
// do initialization stuff here
// return success
return(0);
} break;

case WM_PAINT:
{
// simply validate the window
hdc = BeginPaint(hwnd,&ps);

// end painting
EndPaint(hwnd,&ps);

// return success
return(0);
} break;

case WM_DESTROY:
{

// kill the application, this sends a WM_QUIT message
PostQuitMessage(0);

// return success
return(0);
} break;

default:break;

} // end switch

// process any messages that we didn't take care of
return (DefWindowProc(hwnd, msg, wparam, lparam));

} // end WinProc

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

int Game_Main(void *parms = NULL, int num_parms = 0)
{
// this is the main loop of the game, do all your processing
// here

// for now test if user is hitting ESC and send WM_CLOSE
if (KEYDOWN(VK_ESCAPE))
SendMessage(main_window_handle,WM_CLOSE,0,0);

// Player movement if arrow key pressed
HDC hdc;
HWND hwnd;
hdc = GetDC(hwnd);

if (KEYDOWN(VK_DOWN))
{
// Get previous position
pprevxul = pxul;
pprevyul = pyul;
pprevxbr = pxbr;
pprevybr = pybr;

// Increment position down
pyul = pyul + 5;
pybr = pybr + 5;
} //end if

if (KEYDOWN(VK_UP))
{
// Get previous position
pprevxul = pxul;
pprevyul = pyul;
pprevxbr = pxbr;
pprevybr = pybr;

// Increment position up
pyul = pyul - 5;
pybr = pybr - 5;
} //end if

if (KEYDOWN(VK_LEFT))
{
// Get previous position
pprevxul = pxul;
pprevyul = pyul;
pprevxbr = pxbr;
pprevybr = pybr;

// Increment position left
pxul = pxul - 5;
pxbr = pxbr - 5;
} //end if

if (KEYDOWN(VK_RIGHT))
{
// Get previous position
pprevxul = pxul;
pprevyul = pyul;
pprevxbr = pxbr;
pprevybr = pybr;

// Increment position right
pxul = pxul + 5;
pxbr = pxbr + 5;
} //end if

//Check movement limit

// Top of screen
if (pyul < toplimit)
{
pyul = toplimit;
pybr = pyul + 10;
}
// Left of screen
if (pxul < leftlimit)
{
pxul = leftlimit;
pxbr = pxul + 10;
}
// Right of screen
if (pxbr > rightlimit)
{
pxbr = rightlimit;
pxul = pxbr - 10;
}
// Bottom of screen
if (pybr > botlimit)
{
pybr = botlimit;
pyul = pybr - 10;
}

// End player movement

// TODO - Enemy AI

// Enemy fires bullet at player if no bullet already

if (bulletx == NULL)
{
// Check player is in range
// initialise temp vars
int pdistx = 0;
int pdisty = 0;
// get x distance
if (pxul > exul)
{
pdistx = (pxul - exul);
}
else
{
pdistx = (exul - pxul);
}
// get y distance
if (pyul > eyul)
{
pdisty = (pyul - eyul);
}
else
{
pdisty = (eyul - pyul);
}

// Fire if in range
if ((pdistx < brange) && (pdisty < brange))
{
bulletx = exul;			// Bullet starts at Enemy position
bullety = eyul;
bulletaimx = pxul;		// Bullet aimed at current player position
bulletaimy = pyul;
bdistance = 0;			// Reset distance travelled
}
}

// TODO - Enemy movement

// End enemy movement

// Bullet Processing
if (bulletx != NULL)  // If there is a bullet
{
// Save previous position
pbulletx = bulletx;
pbullety = bullety;

// Check for collision

if ((bulletx >= pxul) && (bulletx <= pxbr))
{
if ((bullety >= pyul) && (bullety <= pybr))
{
//Bullet collision with player
plife = plife - 10;
}
}

// Check for reach aimpoint
if (bulletx == bulletaimx)
{
if (bullety == bulletaimy)
{
//RESET
bulletx = NULL;
bullety = NULL;
bulletaimx = NULL;
bulletaimy = NULL;
}
}

// Move bullet closer to aim point
if (bulletx > bulletaimx)
{
bulletx = bulletx - bulletspeed;
}
if (bulletx < bulletaimx)
{
bulletx = bulletx + bulletspeed;
}
if (bullety > bulletaimy)
{
bullety = bullety - bulletspeed;
}
if (bullety < bulletaimy)
{
bullety = bullety + bulletspeed;
}
//Increment Distance Travelled
bdistance = bdistance + bulletspeed;

//Check for range

if (bdistance >= brange)
{
bulletx = NULL;	// Kill bullet
bullety = NULL;
}
}

// Control speed

Sleep(100);

// return success
return(1);

} // end Game_Main

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

int Game_Init(void *parms = NULL, int num_parms = 0)
{

// NOT WORKING ******************************

HWND hwnd;
HDC hdc;

hdc = GetDC(hwnd);

HPEN black_pen = CreatePen(PS_SOLID, 1, RGB(0,0,0));
HBRUSH blue_brush = CreateSolidBrush(RGB(0,0,255));
// select pen and brush into context
SelectObject(hdc, black_pen);
SelectObject(hdc, blue_brush);
// Erase previous player position
Rectangle(hdc, 100,100,120,150);
DeleteObject(blue_brush);
DeleteObject(black_pen);
ReleaseDC(hwnd, hdc);

// return success or failure or your own return code here
return(1);

} // end Game_Init

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

int Game_Shutdown(void *parms = NULL, int num_parms = 0)
{
// this is called after the game is exited and the main event
// loop while is exited, do all you cleanup and shutdown here

// return success or failure or your own return code here
return(1);

} // end Game_Shutdown

// WINMAIN ////////////////////////////////////////////////
int WINAPI WinMain(	HINSTANCE hinstance,
HINSTANCE hprevinstance,
LPSTR lpcmdline,
int ncmdshow)
{

WNDCLASSEX winclass; // this will hold the class we create
HWND	   hwnd;	 // generic window handle
MSG		   msg;		 // generic message
HDC        hdc;      // graphics device context

// first fill in the window class stucture
winclass.cbSize         = sizeof(WNDCLASSEX);
winclass.style			= CS_DBLCLKS | CS_OWNDC |
CS_HREDRAW | CS_VREDRAW;
winclass.lpfnWndProc	= WindowProc;
winclass.cbClsExtra		= 0;
winclass.cbWndExtra		= 0;
winclass.hInstance		= hinstance;
winclass.hbrBackground	= (HBRUSH)GetStockObject(BLACK_BRUSH);
winclass.lpszClassName	= WINDOW_CLASS_NAME;

// save hinstance in global
hinstance_app = hinstance;

// register the window class
if (!RegisterClassEx(&winclass))
return(0);

// create the window
if (!(hwnd = CreateWindowEx(NULL,                  // extended style
WINDOW_CLASS_NAME,     // class
"T3D Game 1.0", // title
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
0,0,	  // initial x,y
winwidth,winheight,  // initial width, height
NULL,	  // handle to parent
hinstance,// instance of this application
NULL)))	// extra creation parms
return(0);

// save main window handle
main_window_handle = hwnd;

// get the dc and hold it
//HDC hdc = GetDC(hwnd);    NOT REQUIRED???

// initialize game here
Game_Init();

// enter main event loop
while(TRUE)
{
// test if there is a message in queue, if so get it
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
// test if this is a quit
if (msg.message == WM_QUIT)
break;

// translate any accelerator keys
TranslateMessage(&msg);

// send the message to the window proc
DispatchMessage(&msg);
} // end if

// main game processing goes here
Game_Main();

// Draw Player

//get DC
hdc = GetDC(hwnd);
// create pens and brushes
HPEN blue_pen = CreatePen(PS_SOLID, 1, RGB(0,0,255));
HPEN black_pen = CreatePen(PS_SOLID, 1, RGB(0,0,0));
HBRUSH red_brush = CreateSolidBrush(RGB(200,0,100));
HBRUSH green_brush = CreateSolidBrush(RGB(0,255,0));
HBRUSH black_brush = CreateSolidBrush(RGB(0,0,0));
// select pen and brush into context
SelectObject(hdc, black_pen);
SelectObject(hdc, black_brush);

//Wipe Screen
Rectangle(hdc,0,0,winwidth,winheight);

// Erase previous player position
//Rectangle(hdc, pprevxul, pprevyul, pprevxbr, pprevybr);
// Erase previous enemy position
//Rectangle(hdc, eprevxul, eprevyul, eprevxbr, eprevybr);
// Erase previous bullet position if relevant
//if (pbulletx != NULL)
//{
//	Rectangle(hdc, pbulletx,pbullety, (pbulletx + 5),(pbullety + 5));
//}

// Select new pen and brush
SelectObject(hdc, blue_pen);
SelectObject(hdc, green_brush);
// draw new player
Rectangle(hdc, pxul,pyul, pxbr,pybr);

// Change brush
DeleteObject(green_brush);
SelectObject(hdc, red_brush);

// Draw new enemy
Rectangle(hdc, exul,eyul, exbr,eybr);

//Draw bullet
if (bulletx != NULL)
{
Rectangle(hdc, bulletx, bullety, (bulletx + 5),(bullety + 5));
}

// delete the objects
DeleteObject(red_brush);
DeleteObject(blue_pen);
DeleteObject(black_brush);
DeleteObject(black_pen);

// Show score

SetBkColor(hdc, RGB(0,0,0));
SetTextColor(hdc, RGB(0,255,0));
//SetBkMode(hdc, TRANSPARENT);

// Clear text area
sprintf(buffer,"                                                             ");
TextOut(hdc,1,1,buffer,strlen(buffer));
TextOut(hdc,1,20,buffer,strlen(buffer));
// Show info
sprintf(buffer,"Score %d Level %d Life %d",score,level, plife);
TextOut(hdc,1,1,buffer,strlen(buffer));
sprintf(buffer,"bulletx %d bullety %d pxul %d pyul%d",bulletx,bullety,pxul,pyul);
TextOut(hdc,1,20,buffer,strlen(buffer));

// release the device context
ReleaseDC(hwnd,hdc);

Sleep(10);

} // end while

// closedown game here
Game_Shutdown();

ReleaseDC(hwnd, hdc);
return(msg.wParam);

} // end WinMain


The drawing in Game_Init doesn't work for some reason, although it complies, and everything else works ok :( :(

##### Share on other sites
Hi,

You'll have to get informations about how the GDI works. You can't paint anything oustide a WM_PAINT handler and hop it will allways be ok. This is your problem here.

Your Game_init() function is called only once before you process any message (not quite true but correct enough to give you a good hint). Once you begin to process message, you receive a WM_PAINT (or WM_ERASEBKGND) and what you painted on the window vanishes. Since you get this paint message when you show the window, you never see what you want to paint.

To help your, remember this: if you need to draw something that will need to be visible on the window at any time, then you should draw it in the WM_PAINT handler.

Of course, your Game_main() can draw onto the window too - but once you'll get a paint message, all the modifications will vanish.

HTH,

##### Share on other sites
You can draw anytime you want, you don't have to wait for WM_PAINT. You do have to be more aware of how Windows works though if you start taking shortcuts.

I suspect your biggest problem is that you're ignoring compiler warnings about hwnd not having been initialized. There are two places (including in Game_Init) where you do:

HWND hwnd;
hdc = GetDC(hwnd);

which is meaningless because you haven't initialized hwnd. If wouldn't be surprised if you're getting a NULL hdc and thus all the calls fail after that point.

Another that might be getting you is batching. GDI optimizes how many times it has to go back and forth to the kernel by batching up calls and doing them all in one go. You can see this if you try to single step through the code and stuff doesn't appear instantly. You can disable batching by calling GdiSetBatchLimit(1). You should only use this for debugging though - if your app doesn't work right without playing with the batching then you have a bug.

##### Share on other sites
Quote:
 Original post by Anon MikeYou can draw anytime you want, you don't have to wait for WM_PAINT. You do have to be more aware of how Windows works though if you start taking shortcuts.

Obviously not: you can't paint anything before you show the window. Or, if you prefer, it won't be that useful. Of course, you can paint anything when you want. Read what I wrote once again: i did not say that you can't paint oustise of a WM_PAINT handler, I said 'You can't paint anything oustide a WM_PAINT handler and hope it will allways be ok'. I know my wording is poor, but it simply means that anythinhg you have painted outside a WM_PAINT handler will be erased by the new paint message. Therefore, what you painted will disapear. I hope I am aware of how windows works, even if I'm surely not good enough to write correct english.

I still agree with you: cornflake forgot to init his hwnd. I guess he wanted to use main_window_handle instead of hwnd - copy-paste code, isn't it?. But again, he must not do any painting in Game_init(). This is Not Good At All (tm). Everything will be erased after the first paint message.

Regards,

##### Share on other sites
Quote:
 Obviously not: you can't paint anything before you show the window. Or, if you prefer, it won't be that useful. Of course, you can paint anything when you want. Read what I wrote once again: i did not say that you can't paint oustise of a WM_PAINT handler, I said 'You can't paint anything oustide a WM_PAINT handler and hope it will allways be ok'. I know my wording is poor, but it simply means that anythinhg you have painted outside a WM_PAINT handler will be erased by the new paint message. Therefore, what you painted will disapear. I hope I am aware of how windows works, even if I'm surely not good enough to write correct english.Regards,

I'm pretty new at this GDI stuff but I don't think what you are saying is entirely correct. WM_PAINT will only paint what you tell it to do, so if you don't have any paint code in it (like the code from the first post) nothing should be deleted. And you don't have to paint to a window, you can easily write to a system DC which is memory (i.e. double buffering, off screen write).

Maybe it's just how I use windows messaging ... I have a very simple game engine that handles all my windows messages for me. The WM_PAINT simply passes it's device context to the function that actually does the painting (not in the game engine) and is only called (in my case) when I InvalidateRect(..).

Or perhaps you mean that WM_PAINT will erase everything when the window is minimized or resized. Ahh I bet that is what you meant! :)
If so then disregard the above.. Just thinking out loud!

##### Share on other sites
Quote:
Original post by HughG
Quote:
 Obviously not: you can't paint anything before you show the window. Or, if you prefer, it won't be that useful. Of course, you can paint anything when you want. Read what I wrote once again: i did not say that you can't paint oustise of a WM_PAINT handler, I said 'You can't paint anything oustide a WM_PAINT handler and hope it will allways be ok'. I know my wording is poor, but it simply means that anythinhg you have painted outside a WM_PAINT handler will be erased by the new paint message. Therefore, what you painted will disapear. I hope I am aware of how windows works, even if I'm surely not good enough to write correct english.Regards,

I'm pretty new at this GDI stuff but I don't think what you are saying is entirely correct. WM_PAINT will only paint what you tell it to do, so if you don't have any paint code in it (like the code from the first post) nothing should be deleted.

Again, I simplified. But you'll have to take care: in most cases, the WM_ERASEBKGND message is sent before WM_PAINT - and if you don't overide it, it will erase all your window background.

Quote:
 Original post by HughGAnd you don't have to paint to a window, you can easily write to a system DC which is memory (i.e. double buffering, off screen write).

Yes, but until you'll blit this onto a window, you'll have to admit that it won't be visible :)

Quote:
 Original post by HughGMaybe it's just how I use windows messaging ... I have a very simple game engine that handles all my windows messages for me. The WM_PAINT simply passes it's device context to the function that actually does the painting (not in the game engine) and is only called (in my case) when I InvalidateRect(..). Or perhaps you mean that WM_PAINT will erase everything when the window is minimized or resized. Ahh I bet that is what you meant! :) If so then disregard the above.. Just thinking out loud!

No, no need to disregard, you are right - even if I was speaking of complete repaint of the window. It's just that the OP showed little knowledge of the Win32 API (no offence intended). Therefore I tried to be synthetic and deliver informations. I must admit that I somewhat failed :/

Regards,

##### Share on other sites
1) You're right - I don;t have a lot of knowledge

2) I try to copy-and-paste code as little as possible, and only when I understand what it does. The original program was from an example which I understood, but it only drew things in the main event loop.

I am now trying to draw everything in the Game_Init, Game_Main functions as I understand this is where it should be done.

The HWND hwnd; line is ther ebecause before I put it there the code wouldn;t compile, saying 'hwnd' not initialised or something similar. Evidently this is the wrong way to correct this problem, but I still don't see how to get the drawing to work in the Game_Init or Game_Main without WM_PAINT overwriting.

And I understand you don't want to just give me code to do it, so hints are fine. Maybe I need to strip out the game logic and just get drawing working 1st...

##### Share on other sites
Hello,

Quote:
 Original post by cornflake1) You're right - I don;t have a lot of knowledge

That's not a problem :). I was a beginner once ;). Me == blunt and you don't deserve it. Apologies.

Quote:
 Original post by cornflake2) I try to copy-and-paste code as little as possible, and only when I understand what it does.

That's a very good idea.

Quote:
 Original post by cornflakeThe original program was from an example which I understood, but it only drew things in the main event loop. I am now trying to draw everything in the Game_Init, Game_Main functions as I understand this is where it should be done.

The Game_main() is well suited for rendering but, again, Game_init() is not a good place. It is a one shot call made before you begin the message loop, and you cannot be sure that everything you draw in Game_init() will stay on the screen.

Quote:
 Original post by cornflakeThe HWND hwnd; line is there because before I put it there the code wouldn;t compile, saying 'hwnd' not initialised or something similar.

Yes, of course. This is because - in your code - you are using hwnd instead of main_window_handle.
Quote:
 Original post by cornflakeEvidently this is the wrong way to correct this problem, but I still don't see how to get the drawing to work in the Game_Init or Game_Main without WM_PAINT overwriting.

If you want a simple answer, you could replace your occurences of HWND hwnd; by HWND hwnd = main_window_handle;. Another solution is to use main_window_handle instead of hwnd in both Game_init() and Game_main().

Quote:
 Original post by cornflakeAnd I understand you don't want to just give me code to do it, so hints are fine. Maybe I need to strip out the game logic and just get drawing working 1st...

I can give you either hints or code, but if I give you code, it will probably lower your personnal satisfaction - even if you understand it - because you may think that you didn't find the solution by yourself. Therefore I try to give you hints before giving you some code.

As I look at code, I begin to think that your Sleep(100) in Game_main() is a bit harsh: it will not allow you to draw more than 10 frame/s. there are better way to limit the speed of the game - some of them had already been discussed here on gamedev, so I guess you can search the archives (search for GetTickCount() for example). If you need further informations, we'll help you ;)

HTH,