Hi, I'm not sure if this is the correct part of the forum for this type of question, but I received amazing support with my previous 'magic pink' question, so I thought I'd dump this here.What is the best way to take keyboard commands and turn them into sprite movements. Take tetris for example, only instead of rotating the blocks, you only need to move them from right to left. How would one go about doing this?Here are some ideas I've had, I know they are crummy, but I'm having a major brain-fart here.???

Notice I removed the "else" on the second if. Consider this scenario - the player is pressing both left and right at the same time. With your version, only the "right" would be detected and the block would move right. In reality, I think a more... "expected" behavior would be that left and right balance each other out and the block shouldn't move.

Keep in mind after you do all the movement based on the keyboard state you'll need to do bounds checking, of course.

Also, you shouldn't specifically say "block.posY = block.posY + 64", but use a constant for 64. In the header file, put "#define BLOCK_SIZE 64" or something similar. Then you can simply use "block.posY = block.posY + BLOCK_SIZE", which is the prefered way to do it. It prevents minor errors such as typos (63 or 65 instead of 64 would cause quite a problem in your case), and is very useful if you ever wish to change the block size. You would then only need to change it in one location (the #define) rather than possibly 100s.

Aside from that, as Birdeeoh said, you need to do bounds checking. I prefer to do pre-checking, though, when available.

Birdeeoh:I did actually have dual if's in my code instead of the if-else, I have no idea why I put that.

I believe in my original post I had my X's and Y's mixed up. If I want horizontal movement, that is moving it from side to side, that is the X axis right?

I'm glad to see that I at least have some idea of what I'm trying to accomplish here. Bounds checking was something that naturally followed keyboard input. In my actual code i have it in the same function.example:

On second inspection yes you had x and y axis mixed up. I wasn't paying attention to that detail because you had the gist of it correct!

In the followup code you posted, I have a reccommendation, as well as a question about a few possible typos...For key[KEY_RIGHT] you have the checkif(block.posX - BLOCK_SIZE < RIGHT_BOUNDS)and for key[KEY_LEFT] you have if(block.posX - BLOCK_SIZE > RIGHT_BOUNDS)I think you're missing a LEFT_BOUNDS and/or have your greater/less than signs backwards...

But my general reccommendation would be to do the whole thing this way -

My first thought was "what? are you crazy?" then I realized we're talking about a tile-based coordinate system and the example given was tetris style. In that case, indeed moving the block one space over every single frame would be WAY too fast

However your approach lends itself to a problem - have you ever played a tetris-like game where you hold the key down and you KNOW they are relying on the key-repeat because there's a long delay before the block starts moving?

To do this, you'll need to set it up properly first. Perfect function, Allegro has -voidset_keyboard_rate(intdelay, int repeat);Tinkering around for the right repeat rate is up to the programmer. But the delay should most definitely be zero - that is, for a Tetris-type application.

Notice the big difference between if( keypressed()) and while( keypressed()) - it's very possible the user could press two keys, press the same key twice, or the repeat rate will put two keys in the buffer inbetween two distinct frames. If you only handle one keypress per frame the buffer is going to back up. Gotta empty it out every iteration of your main loop

Good catch Jonny - I wasn't even thinking about the specific application (assuming it is actually tetris-like)

Birdeeoh:I'm dumb!, yes i did get those all muddled up, i was thinking too hard about if i was supposed to be using X's or Y's.. in programming sense... i don't think X's and Y's matter, only what u assign to them, but i guess its best not to confuse myself any further on that matter.

As an answer to the 'question' yea it is very much like a tetris clone. More like super puzzle fighter (if you've ever had the pleasure of playing that great arcade game). With the bounds checks you've all been so kind to post previously, which one would work best for making sure that as soon as the edge of the block touches the 'wall' (right or left boundary), it won't let the block precede any further.

AT and original PS/2 keyboards were hardware limited by the mobo's keyboard controller. Newer PS/2 controllers and (all?) USB keyboards are 100% interupt driven and most of the keyboard handling is by the Operating System.

In Windows the max rate configurable by the Keyboard Control Panel is 30 cps, but with dozens of keyboard manufacturer utilities, 3rd party utilities, and hacking the registry you can crank that WAY up.

And programatically things can get pretty insane - I just ran a simple test with set_keyboard_rate( 1, 1 ), meaning the repeat starts after 1 ms and the key repeats each ms. I ran a two second test and grabbed 2001 keypresses. The 1000 cps limit is due to Allegro's handling of it - it can be higher at the OS level.

But the point isn't neccessarily the insane rates POSSIBLE with repeating one key so much as multiple key presses. If you mash the keyboard you can easily get more than 1 keypress in, say, 1/60th of a second. Even with just two fingers on two different arrow keys it can easily happen.

Plus other programs can be inserting keys into the buffer programatically...

I suppose the whole point is that it's just best to program for what CAN happen rather than what you expect to happen because both the real world environment and the user will violate your expectations

Interesting discussion....Birdeeoh, can you explain to me what "readkey() >> 8" really means and how it works in the code you had posted earlier? I understand how that shifts the value of what readkey() returns so that it returns the scancode (or whatever)...but, what is this about the delay and such?

C Bird, X generally represents horizontal positions, and Y represents vertical positions. Z would be depth, but in 2D there is no depth. Although, if you really wanted to mix up your variable names, yes, you could youse Y to represent the horizontal position, and X for vertical, but that's a bad practice. As for the "best" method of preventing the block from going out of bounds, I would suggest a pre-test (as I posted code to above). A post-test also could work, but I think it makes more sense to prevent the block from moving outside boundaries, than allowing it to and snapping it back into boundaries. Either way, this would happen so fast that the user would not notice it had gone out of boundary. It's all up to you and what you feel more comfortable with.

readkey() returns an int with two pieces of data in it. The lowest byte is the ASCII code of the keypress that was in the keyboard buffer. This is the char that you would put in a c-style string (ie a const char*)

The next byte is the actual scancode read off the keyboard. You access the second byte by shifting the integer over 1 byte, hence the readkey() >> 8

The scancode differs from the ASCII code in that the scancode represents a key on the keyboard whereas the ASCII char represents a (usually) printable character. IE KEY_B is the scancode but the char is either 'b' or 'B' depending on if shift or caps lock was active when KEY_B was entered into the buffer.

Now the whole keypressed() and readkey() system refers to the keyboard buffer. A keypress is entered into the buffer whenever someone presses and then releases a key. This is the behavior you can capture by hooking into the keyboard_callback function.

But if you press a key and hold it down, the operating system takes over and starts generating repeated keypresses for that character. For every repeat the OS decides on, it enters that key into the keyboard buffer as if the user had pressed and released the key.

There's two aspects to the key repeat - the delay and repeat rate. The delay is how long you have to hold down the key before the OS starts generating repeats. The rate is... the rate! How many keypresses the OS generates per second.

I think a common setup in the Windows control panel is a 250 ms delay and 30 repeats per second. But using set_keyboard_rate( delay, rate ) in your Allegro app you can customize it to a wide range of possibilities.

Thanks. I learned a little bit from you there, although it was a little bit more than needed :p Based on what you had said earlier, I thought that you were saying readkey() automatically included some sort of delay mechanism (other than the key_up thing). Thanks for clarifying though.

I don't the the keypressed() approach is such a good idea; usually, you want to increase movement speed with level, based on a game-logic-specific variable. Since the keyboard routines are part of the input API, you'll need 2 function calls to set a new game speed (e.g. when going up one level). A better solution would be to implement the key repeat manually by using a counter. Something like this:

For one, tetris-style games don't ALWAYS change the speed as the level advances. I remember many a tetris clone where I could rely on the key-repeat action at earlier levels but at later levels I have to start going into turbo-key-press mode manually. And plenty where the auto-repeat was too fast for earlier levels but the only thing to rely on later

But if this is a desirable thing for the programmer...

I always cringe a little regarding locking anything into the logic frame rate without taking into account that the logic rate can be quite dynamic over the course of execution =/

set_keyboard_rate() is such an inexpensive call, it may be worth looking into.

But, I agree a custom delay is quite reasonable - for a case this simple I'd probably hook into keyboard_lowlevel_callback and keep my own motion flag

Ok, so i've got a square moving from side to side on my screen, but he's not disappearing from his previous locations. How do i go about clearing his trails, but keeping all the previously dropped squares where they lay?

Ok, so i've got a square moving from side to side on my screen, but he's not disappearing from his previous locations. How do i go about clearing his trails, but keeping all the previously dropped squares where they lay?

Are you drawing your sprite directly to the screen?

There are several different ways to draw/output your graphics (e.g. double buffering, page flipping, tripple buffering, etc.). Double buffering is pretty easy to understand. In a few words, you draw everything to the buffer after all your logic occurs each game loop and then draw the buffer to the screen. Before you start drawing to the buffer, do:

// Now draw everything to the buffer
clear(buffer);// run all draw routines

so you drew to the buffer and than cleared it w/o first blitting it to the screen? for some reason that doesn't make any sense to me, i thought you would draw to the buffer, blit the buffer to the screen, and then clear the buffer out.

Not the most efficient way but the easiest way would be to store the location of each fallen block or store a 2d array representing the tilemap that is the game board and which spaces are filled.

How else will you do "hit detection" with the currently active block if you don't know the layout of the gameboard?

Then each refresh cycle its quite easy to just redraw the entire gameboard - speed shouldn't be an issue with a game this simple, and with double buffering/page flipping you won't see any screen artifacts.

i thought you would draw to the buffer, blit the buffer to the screen, and then clear the buffer out.

My pseudocode was somewhat misleading. I clear the buffer, draw to the buffer, then blit to the screen. My code:

// Now draw everything to the buffer
clear(buffer);// run all draw routines

meant this: "Now draw everything to the buffer" was meant to be a comment, not a command, "clear(buffer)" means clear(buffer), and "run all draw routines" means draw everything you are going to draw to the buffer at this point.