[WIN32] Displaying Text on Key Press

Hello again CPP Forums,
I do not know much about WIN32, just a few basics, I usually am a console app kind of guy; however, today I felt it necessary to climb out of my hole and try something new.

I ran into problems...

I thought this would be very simple to do and tried to have text appear in the center of the screen when the user pressed Escape on their keyboard. This was one of my many attempts:

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
        case WM_CLOSE:
            DestroyWindow(hwnd);
        break;

        case WM_DESTROY:
            PostQuitMessage(0);
        break;

	case VK_ESCAPE:
	    GetClientRect(hwnd, &rc);

	    SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT));
	    SetBkMode(hdc, TRANSPARENT);
	    SetTextColor(hdc, RGB(255, 0, 0));

	    DrawText(hdc, Resume.c_str(), Resume.length(), &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
	break;

        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}


These are some things I was forced to define outside of the Callback Function (at the top of the source):

Code:
1
2
3
4
HDC hdc;
RECT rc;

string Resume = "Resume";



Question Put Simply:

How do I display text in the center of the screen when a key is pressed in WIN32 C++?
Last edited on
Before I start to answer:

Judging from your username I'm guessing you're doing this to make steps towards making a game. If that is the case, I want to discourage you from using WinAPI for that. Using WinAPI to write a game is clunky, awkward, difficult, nonportable, and slow (as in... the game will be slow)

I recommend you consider a lib designed for that kind of thing... like SFML:
http://sfml-dev.org/


To answer your immediate question:

VK_ESCAPE is a Virtual Key code (hence why it starts with VK)
It it not a Windows Message code (hence why it doesn't start with WM)

Windows will send your program a message when a key is pressed... but the message will not be the key code... it will be a key down message. Specifically, WM_KEYDOWN.

When the message is WM_KEYDOWN, the wParam will be your keycode. So you would check to see if wParam == VK_ESCAPE.

Reference for WM_KEYDOWN:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms646280%28v=vs.85%29.aspx


Other problems:
I don't see where you're initializing your 'hdc' handle... but you're probably doing it wrong.

Also... you have "console mentality" where you think you can put it on the screen once and have it stick there -- Windows doesn't work like that. Even if this drew as you'd expect, you could drag another window over your program and the text would vanish (but only the part of it that got covered up).

If you are drawing to your window outside of a WM_PAINT message, then the way to do it is:

1) Call GetDC() to get your window handle's DC
2) Do any drawing to the dc
3) Call ReleaseDC immediately after you're done drawing to release it.

Also... any components of the DC you change with SelectObject should probably be reverted before you release the DC. This often won't matter, but depending on the circumstance it might ... and it's good practice regardless.


As for the problem of having the text "stay there" and not get erased when you cover it with another window... that's a bigger topic. If you're interested lemme know and I'll reply. But I don't want to put too much in this first response =P
The username is actually an inside joke, about 3 years old xD; however, this code is for a Text-Based Game. Thank you for the advice, I am not sure if the cited link is used for that too...

Ok... So, I understand what VK and WM are, thought I could call VK in that instance too... oops.

I initialized hdc outside of any function, just under using namespace std; not sure if that is correct... If there is a way for me to accomplish this with WM_PAINT, I would love to know, but I couldn't figure out how to change it after it was there...

This is going to be for the "Pause" menu of the game, so... It would be helpful if it stays on the screen...

Thank you for the reply!
If there is a way for me to accomplish this with WM_PAINT, I would love to know, but I couldn't figure out how to change it after it was there...


This is more "console-itis" (I could make up terms for this all day). Typically in programs you need to separate your drawing code and your logic code.

on a side note... this is one of the reasons I try to dissuade people from starting with the console... it teaches you all sorts of things that you effectively have to 'un-learn' because you can't use them anywhere else.

If pressing escape pauses the game... then all you really need to do is set a variable that indicates the game is paused. Then in your drawing code... you would check that variable and draw the scene differently if it is set.


Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// broad scope variables.  Global in this example to keep it simple, but I hate
//  globals
bool isPaused = false;  // true when the game is paused

// function to draw the current scene.  Notice it takes the HDC as a parameter
//  (does not use a global)
void DrawScene(HDC screen)
{
    if( isPaused )
    {
        // print text to 'screen' here
    }
    else
    {
        // ...
    }
}

/* Then in your WM_PAINT handler:  */
case WM_PAINT:
{
    PAINTSTRUCT ps;
    HDC screen = BeginPaint(wnd,&ps);
    DrawScene(screen);  // call our function to draw the scene
    EndPaint(wnd,&ps);
}break;


/* Then... to pause... */
case WM_KEYDOWN:
    if(wParam == VK_ESCAPE)
    {
        isPaused = !isPaused;  // pause or unpause
        InvalidateRect(wnd,NULL,TRUE);  // tell Windows to redraw the window
           // ie:  it will send another WM_PAINT message after this call
    }
break;



EDIT:

This is going to be for the "Pause" menu of the game, so... It would be helpful if it stays on the screen...


Just wondering... why would you need to pause a text-based game?


EDIT 2:

this code is for a Text-Based Game. Thank you for the advice, I am not sure if the cited link is used for that too...


As long as I'm giving out free advice.... Text-based games are actually considerably harder to write properly than simple graphical games.

If you are making a text based game because that's what you're into... then go for it.

But if you are making a text based game because you think it'll be easier -- reconsider. A simple vertical shooter with graphics is actually much, much, much easier to make.
Last edited on
Thank you very much, about to test that!
 
Just wondering... why would you need to pause a text-based game? 


It's not really a pause, in most cases, it's mainly to pull up the settings and quit buttons, also, for learning experience; however, in most TB games, the battles work with turn based logic, I was planning on trying something new, I have a few unrefined ideas, one of them being like a classic swtor or WoW <-- MMORPGs, where the player has skills they can learn and a customizable hotbar, they click on their moves to attack, need stamina for each move, on and on, all the while there is a second thread handling attacker attacks, so they are attacking at the same time, unless the player hesitates to attack, or some moves have more recovery time, stuns on and on. It would also stop dialogue

EDIT::

As long as I'm giving out free advice.... Text-based games are actually considerably harder to write properly than simple graphical games.

If you are making a text based game because that's what you're into... then go for it.

But if you are making a text based game because you think it'll be easier -- reconsider. A simple vertical shooter with graphics is actually much, much, much easier to make.


Actually I have seen many, many other posts, also claiming graphical is easier, I do enjoy TB games though, and just thought it would be an okay place to start, I am sure, I will eventually go to a graphical game, maybe even just a graphical remake of this game.

EDIT 2::
So I tested it, and it seems to work, except for the draw code I found on google, this is the code I found, could you scan through it for any errors or share any examples you have?

1
2
3
4
5
6
7
		RECT rc;

		SelectObject(screen, GetStockObject(DEFAULT_GUI_FONT));
		SetBkMode(screen, TRANSPARENT);
		SetTextColor(screen, RGB(0, 0, 0));

		DrawText(screen, Resume.c_str(), Resume.length(), &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
Last edited on
they click on their moves to attack, need stamina for each move, on and on, all the while there is a second thread handling attacker attacks


So kind of like an RTS?

Just FYI: you probably would not want to use multiple threads for that. You'd do it all in the same thread -- just updating everything serially.

So I tested it, and it seems to work, except for the draw code I found on google, this is the code I found, could you scan through it for any errors or share any examples you have?


Reference page for DrawText:
http://msdn.microsoft.com/en-us/library/windows/desktop/dd162498%28v=vs.85%29.aspx

You are telling it to draw the text in the center of the 'rc' rect.
But you never specify the 'rc' rect -- you're giving it an uninitialized/garbage rect.



EDIT:

More free advise:

When you find sample code on the internet... instead of copy/pasting... look up what each function does so that you can actually see how it works. MSDN is a great source for WinAPI reference docs.
Just google "msdn NameOfTheFunction" and it'll likely be the first hit.

A little research to see how these functions work and what all the parameters mean can go a long way in wrapping your head around what the code actually does.

Of course... doing this might not be practical if the code sample is a cluster of like a dozen lines of code -- if the code is overwhelming then you can probably get away with a black-box copy/paste job. But try to avoid that.


EDIT2:

Also.. You want DrawTextA (note the A at the end) if you are using normal strings.
DrawText takes TCHARs, not chars.

That's another complication with WinAPI: the whole TCHAR fiasco.
Last edited on
Real time strategy? I suppose, in a way.

Update everything serially? Not sure I understand, I can't get them to attack at the same time without threading... can I?

ok, how do I specify the coords for the rect?

I apologize, I am extremely new to WIN32 API


When you find sample code on the internet... instead of copy/pasting... look up what each function does so that you can actually see how it works. MSDN is a great source for WinAPI reference docs.
Just google "msdn NameOfTheFunction" and it'll likely be the first hit.

A little research to see how these functions work and what all the parameters mean can go a long way in wrapping your head around what the code actually does.

Of course... doing this might not be practical if the code sample is a cluster of like a dozen lines of code -- if the code is overwhelming then you can probably get away with a black-box copy/paste job. But try to avoid that.

I usually do, sometimes I do the unthinkable and don't learn though :/


Also.. You want DrawTextA (note the A at the end) if you are using normal strings.
DrawText takes TCHARs, not chars.

That's another complication with WinAPI: the whole TCHAR fiasco.

Okay, I'm still not sure how to set the coords though....
Last edited on
Update everything serially? Not sure I understand, I can't get them to attack at the same time without threading... can I?


That depends on what you mean by "at the same time".

'Serially' = everything takes turns, going one at a time.
'Parallel' = everything runs at the same time.

The problem with parallel is that it's tricky. Damn tricky. And in cases where multiple jobs are sharing the same information (like a map... or another object's position)... you almost always have to guard accesses so that they don't actually happen in parallel.

Since games tend to have lots of moving parts that all interact with each other, writing parallel code is extraordinarily difficult. Which is why most games, AFAIK, don't do it (at least not for the game logic).


So my point is... you don't really want things to happen at the exact same time in your game. You just want them to happen really really close to each other.





Let's back up a minute and look at what I suspect you're thinking of doing:
1
2
3
4
5
6
7
8
9
// Called once by a separate thread
void DoEnemyStuff()
{
    while( alive )
    {
        AttackPlayer();
        Sleep(1000);
    }
}


The idea being that the enemy attacks the player, waits a second, attacks again, waits, etc.

This might seem straightforward and simple... but really it's horrible. Pretty much the only way to make this approach work would be to use a separate thread. But then you'd need to put each enemy in its own thread.. and you'd have to carefully synchronize dozens or even hundreds of threads (not easy).

So instead of crap like that... you typically update things serially. The idea being that your enemy just takes a small step through his logic (aka: an "update")... rather than doing all of it at once. Then your main program can merely update all objects every frame.

Something like this:
1
2
3
4
5
6
7
8
9
10
11
12
// Called once every frame
void DoEnemyStuff()
{
    if( time_to_next_attack < 0 ) // if it's time to attack
    {
        AttackPlayer();  // attack the player
        time_to_next_attack += 1000; // then wait 1 second before attacking again 
    }

    time_to_next_attack -= time_since_last_update; // count down based on how much
       // time has passed
}



Typical, simplistic game loop:

1
2
3
4
5
6
while(game_is_running)
{
    ProcessUserInputAndWindowMessages();
    UpdateAllObjects();
    DrawTheScene();
}


In normal games, the entire screen is redrawn every frame. If you have a fixed framerate (like say 60 FPS), then you can simplify things and update all your objects once per frame.




Wshew. That was probably a lot wordier than it needed to be, but hopefully you get the idea.


ok, how do I specify the coords for the rect?


RECTs have a 'left', 'top', 'right', and 'bottom' value. Just set those to whatever you want:

1
2
3
4
rc.left = 0;
rc.top = 0;
rc.right = 200;
rc.bottom = 100;


Now the text will be drawn in a 200x100 rectangle in the upper-left corner of the screen (position 0,0)
Okay, I can see where you are going with the threading thing, which is why I was thinking of approaching that from a very different standing point...

I was planning on doing something along the lines of all enemies logic in one thread, stats, names, whatever (except ability functions and such), when running, the thread first checks which enemy they are fighting, as this is a text based game, it won't need separate threads per enemy, there isn't really a "World" however in a graphical game with wandering entities and such, your idea is far better.

Ahh just what I was looking for, implementing now, I'll let you know what happens!

EDIT::
At long last, the text is there; however, the tweaking of the position to get center screen will be very tedious...
Last edited on
DrawText will center it in whatever rect you give it.

If you want to center it on your window, then you can give it the rect of your window. GetClientRect() will give you the rect of the client area of your window (the drawable part)
You life saver! Anyways, how can I change font/font size?
Create a font that you want to use with CreateFont:

http://msdn.microsoft.com/en-us/library/windows/desktop/dd183499%28v=vs.85%29.aspx


Then select it into the DC with SelectObject.
Then, after drawing, revert the original HFONT back into the DC:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// do this once at program startup -- no need to create the font every time you redraw:
HFONT myfont = CreateFont( ... fill in params here ... );


// then when you draw...
HFONT prevfont = (HFONT)SelectObject( screen, myfont );
    // draw you text here.

//  .. when done drawing...
SelectObject(screen, prevfont);


// Then... when done with the font (program quit)... be sure to destroy the font you
//  created
DeleteObject(myfont);

Last edited on
Ok, just got done reading through the whole article, that function is practically burned in my brain now, I tested it out and it works wonders. I did it with a default font, Time New Roman, would this work with a non-preinstalled font, for instance, a custom font?

EDIT::
Works! Thanks again!
Last edited on
Topic archived. No new replies allowed.