Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » C++ #define question

This thread is locked; no one can reply to it. rss feed Print
C++ #define question
AceBlkwell
Member #13,038
July 2011
avatar

All,

Is it possible to place two values on a #define.

#define Q 4433, 4465

Long story but I'm trying to allow for arrows to be used in my program so I have to use key codes or scancode (or what ever they are called) but I don't want any if statements to have to look for upper and lower case letters separately.

thanks
Ace

torhu
Member #2,727
September 2002
avatar

Are you using Allegro 5? Just check the keycode field of the event. Look at ex_keyboard_events if you need some hints how to do it.

Eric Johnson
Member #14,841
January 2013
avatar

You don't have to worry about character case if you're just going for arrow keys. Use something like this in your game loop (assuming Allegro 5):

#SelectExpand
1ALLEGRO_EVENT event; 2 3al_wait_for_event(event_queue, &event); 4 5if (event.type == ALLEGRO_EVENT_KEY_DOWN) { 6 7 switch (event.keyboard.keycode) { 8 9 case ALLEGRO_KEY_UP: 10 11 // Move the player up. 12 break; 13 14 // Check other keys here. 15 } 16}

bamccaig
Member #7,536
July 2006
avatar

You can put anything you want in a CPP macro (#define), but it's just static text. It will not magically be expanded by the preprocessor into multiple checks. Show us your code and will have a better idea what you're trying to do. In general, if case is all that matters, you can lower-case both sides and compare them (or upper-case both). Show us code and we might be able to be more helpful.

Chris Katko
Member #1,881
January 2002
avatar

How... how would that work?

#define Q 4433, 4465

if(key[Q])
{
}

becomes

if(key[4433, 4465]) //not valid C
{
}

If you want for upper/lower case at the same time... just use keycodes instead of scancodes which aren't case sensitive...

https://www.allegro.cc/manual/5/keyboard.html

if(al_key_down(kbdstate, ALLEGRO_KEY_A))//doesn't care if 'a' or 'A'

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

AceBlkwell
Member #13,038
July 2011
avatar

Thanks All,

Chris, I didn't think it would work but was hoping there was another trick I could use.

Unfortunately I'm using Allegro 4. I will include the code.

The issue in short is, during my game Menu, Credits, & Instructions screens, I have a set of Atari Adventure looking Dragons that go back and forth along the bottom of the screen while waiting on the user entry. At the time I wrote this I just dealt with letter choices so I have a CHAR called RESP being returned to main. In this case I used toupper after readkey so any IFs involved in the Menu functions only had to look for the upper case of a letter (C, I, P, Quit). Later I got the idea to scroll the Instructions, within a widowed screen. Now I have to have arrow keys to scroll. Arrow keys will not work with CHAR variables. I have to use INT and scancodes. I can make it work, but with out the "toupper" option I've just doubled my checks for Q = 4433 and q = 4465. I was hoping a tricky define might allow for a single macro call Q for both numbers.

In any case, it doesn't sound like it will. I was just looking for simplicity and compact code.

Not it will tell the entire tale but here is the CHAR DRAGONS function that simply moves the dragons while waiting on a key hit. The calling functions actually determine what to do with the return value.

Thanks
Ace

#SelectExpand
1/**************** Function for Screens Key Reads **************************/ 2char dragons(BITMAP* GameBitmaps[], DATAFILE *GameDat) { 3 static int count = 0; 4 static int loop_status = 0; 5 char resp = 'A'; 6 7 clear_keybuf(); 8 9 10 while (!keypressed()) { 11 12 blit(GameBitmaps[BUF_HOLD], GameBitmaps[BUFFER], 0, 0, 0, 0, 640, 13 480); 14 15 if (loop_status == 0) { 16 count++; 17 draw_sprite(GameBitmaps[BUFFER], 18 (BITMAP *) GameDat[DRAGON_LH].dat, 530 - count, 420); 19 draw_sprite(GameBitmaps[BUFFER], 20 (BITMAP *) GameDat[DRAGON_RH].dat, 90 + count, 420); 21 } //End Loop Status 0 22 23 if (count > 440) 24 loop_status = 1; 25 26 if (loop_status == 1) { 27 count--; 28 draw_sprite(GameBitmaps[BUFFER], 29 (BITMAP *) GameDat[DRAGON_RH].dat, 530 - count, 420); 30 draw_sprite(GameBitmaps[BUFFER], 31 (BITMAP *) GameDat[DRAGON_LH].dat, 90 + count, 420); 32 } // End Loop Status 1 33 34 if (count <= 0) 35 loop_status = 0; 36 rest(5); 37 38 blit(GameBitmaps[BUFFER], screen, 0, 0, 0, 0, 640, 480); 39 } //while pressed 40 resp = readkey(); 41 resp = toupper(resp); 42 return (resp); 43 44}

Eric Johnson
Member #14,841
January 2013
avatar

Just curious: why are you using Allegro 4? I've seen a handful of posts related to Allegro 4 recently, but I keep asking myself "why?". I understand that Allegro 4 was out for a long while before 5, and that it's near and dear to a lot of folks. But unless you're trying to make something for DOS, why not use Allegro 5 instead?

Chris Katko
Member #1,881
January 2002
avatar

Allegro 4 should be even easier to read keys!

Check out this one:

https://www.allegro.cc/manual/4/api/keyboard-routines/key

Don't use while(keypressed()).

In general you do something like this:

bool is_finished = false;
while(!is_finished)
{
if(key[KEY_ESC]) is_finished = true; //ready to exit the game


if(key[KEY_UP]) move_guy_up();
//etc
if(key[KEY_W]) move_guy2_up(); //W or w!
//etc

vsync();
blit();
}

And I normally put all keyboard routines into a input() function which merely sets flags, and all EFFECTS of those flags are in a logic() function that gets called next.

So in my games it's normally:

#SelectExpand
1void main() //this is ALL that I put in main. 2{ 3initialize(); //allegro setup routines, loading files, etc. 4execute(); 5shutdown(); 6} 7 8void execute() 9{ 10while(true) //Runs this full loop ONCE per game frame 11 { 12 if( input () )break; //either return 1 to indicate quit, or I set a global "is_finished" variable 13 logic(); 14 draw(); 15 } 16} 17 18bool player1_wants_to_move_upward; // global for simplicity (no time to explain dependency injection) 19 20int input() 21{ 22if(key[KEY_ESC])return 1; //if time to quit game 23 24//other keys 25// NOTE, input SETS FLAGS (that are reset per frame). It doesn't directly call physics or logic updates. 26player1_wants_to_move_upward = false; 27if(key[KEY_W])player1_wants_to_move_upward = true; 28} 29 30void logic() 31{ 32if(player1_wants_to_move_upward) 33 { 34 //attempt to move him upward. 35 } 36// NOTE HERE. NO matter HOW MANY times W is pressed within ONE FRAME, the FLAG 37// is only set to TRUE. So this guy won't move upward MORE TIMES if a player 38// can press UP more times during a slow frame rate. This is IMPORTANT for most 39// games, like jumping, where you don't want someone to rapidly press SPACE to 40// jump higher than they should be able to 41}

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

Audric
Member #907
January 2001

AceBlkwell's use of keypressed() and readkey() looks fine, because it will make use of keyboard repetition : Press and hold the key, it will trigger once, wait a fraction of a second, then start repeating 10 times per second.

As for testing individual keys, readkey() returns an integer already. Just make resp and dragons() integer instead of char, and have the calling code test for :

int val = dragons();
if ((val >> 8) == KEY_LEFT) // for cursor, ESC,...
   ...
if ((val & 0xff) == 'a') // Letter A on keyboard, no matter where it is (works on every keyboard layout)
   ...

AceBlkwell
Member #13,038
July 2011
avatar

Audric,

Thanks for the info. Couple of questions. I'm not familiar with the shifts or offsets (or whatever they are). How do you know which to use or what the offset should be?

val >> 8 or val & 0xff ?? not really for sure what these are. If it's too much to explain, just tell me where to look.

Eric,

I'm using Allegro 4 because it's more console friendly from my perspective. I do want to graduate Allegro 5 but for the sake of my current program, I didn't want to learn event type programming on top getting refamiliar with programming. I haven't even written a program that uses a mouse yet. but I agree it is time to move on. Maybe after this one is done.

Chris,

I appreciate the info on the KEY_(letter) will cover both upper and lower case. That's what I'm looking for

Thanks All,

bamccaig
Member #7,536
July 2006
avatar

It's all in the fabulous manual. Your distribution hopefully came with a copy, perhaps you have to build it, but one version can be found on liballeg.org[1] (or indeed, also there should be one still on allegro.cc).

TFM said:

The low byte of the return value contains the ASCII code of the key, and the high byte the scancode. The scancode remains the same whatever the state of the shift, ctrl and alt keys, while the ASCII code is affected by shift and ctrl in the normal way (shift changes case, ctrl+letter gives the position of that letter in the alphabet, eg. ctrl+A = 1, ctrl+B = 2, etc). Pressing alt+key returns only the scancode, with a zero ASCII code in the low byte.

This paragraph is followed by example code that demonstrates, but I'll blabber one with my own explanation inline.

The >> operator shifts the bits in the left operand (value) to the right by the right operand. In this case, x >> 8 means to move all of the bits in x to the right 8 places, effectively erasing the bottom 8 bits, and moving the 8-32 bits to positions 1-24, respectively (for a 32-bit integer) (using a 1-based index, which is somewhat weird probably).

    0001000101010101 >> 8
           |
           +-------+
                   |
  = 0000000000010001

The & operator is a bitwise-AND. This compares the bits in the left-hand operand with the corresponding bits in the right-hand operand. If they're both 1 then the result is 1. If either is 0 then the result is 0. Just like a standard AND logic gate.

    00010001
  & 01010101
  = 00010001

0xff AKA 255 represented in binary in a 32-bit signed integer is 00000000000000000000000011111111 (all 8 lowest bits set to 1). This is just another trick to discard the parts you don't care about (the upper bits) and keep the ones you do care about (the bottom 8). Any bits in the other operand that are set in the lowest 8 positions are kept, and the rest are thrown away. This leaves you with only the part that you care about.

AceBlkwell
Member #13,038
July 2011
avatar

Thx Bam. I'm some what familiar with the concept from dabbling with assembly.

Last question. Is the shift vs bit wise AND dependent on getting a letter vs KEY or is it just a preference of the programmer?

Thx
Ace

bamccaig
Member #7,536
July 2006
avatar

The bitwise operations are required to extract the information you care about from the readkey function. It returns an integer. Within this one integer are two fields. The ASCII code corresponding to the character is stored in the "low-byte". The scancode is stored in the "high-byte". Where A=ASCII character and S=scancode:

32-bit int:     00000000 00000000 SSSSSSSS AAAAAAAA
               32       24       16        87654321

If you bitwise-AND with 0xFF you get AAAAAAAA. If you shift everything the right 8 bits (1 byte) you get SSSSSSSS. The scancode in the high-byte gets you the "key", whereas the ASCII character is obviously the "letter".

So I guess yes, it is dependent on what information you care about. The scancode should tell you everything (except for maybe Alt?), but you may prefer the ASCII code if you're manipulating text.

Go to: