Это проблема, которая преследует меня в течение многих лет.Как нажимать несколько клавиш одновременно с использованием событий в режиме реального времени? (Allegro 5)
Вот мой game.h и game.cpp файлы:
game.h
#ifndef GAME_H_INCLUDED
#define GAME_H_INCLUDED
#include "init.h"
ALLEGRO_BITMAP *load_bmp(path *s);
struct Actor {
const char *path;
ALLEGRO_BITMAP *bmp;
int x;
int y;
int speed;
};
void init_game_bitmaps();
void draw_game_bitmaps();
extern map<string, bool> key_states;
void init_key_states();
void check_states();
void control_actor(Actor *target, int speed);
extern Actor player;
#endif // GAME_H_INCLUDED
game.cpp
#include "game.h"
ALLEGRO_BITMAP *load_bmp(path *s) {
ALLEGRO_BITMAP *bmp = nullptr;
bmp = al_load_bitmap(s);
if (!bmp) {
al_show_native_message_box(display,
"Fatal Error!",
"Failed to load: " ,
s,
NULL,
ALLEGRO_MESSAGEBOX_ERROR);
al_destroy_display(display);
return nullptr;
}
return bmp;
}
map<string, bool> key_states;
void init_key_states() {
key_states["UP"] = false;
key_states["DOWN"] = false;
key_states["LEFT"] = false;
key_states["RIGHT"] = false;
}
void check_states() {
auto key = e.keyboard.keycode;
for (auto it = key_states.begin(); it != key_states.end(); ++it) {
if (e.type == ALLEGRO_EVENT_KEY_DOWN) {
if (key == ALLEGRO_KEY_UP) {
if (it->first == "UP") {
it->second = true;
}
}
if (key == ALLEGRO_KEY_DOWN) {
if (it->first == "DOWN") {
it->second = true;
}
}
if (key == ALLEGRO_KEY_LEFT) {
if (it->first == "LEFT") {
it->second = true;
}
}
if (key == ALLEGRO_KEY_RIGHT) {
if (it->first == "RIGHT") {
it->second = true;
}
}
} else if (e.type == ALLEGRO_EVENT_KEY_UP) {
if (key == ALLEGRO_KEY_UP) {
if (it->first == "UP") {
it->second = false;
}
}
if (key == ALLEGRO_KEY_DOWN) {
if (it->first == "DOWN") {
it->second = false;
}
}
if (key == ALLEGRO_KEY_LEFT) {
if (it->first == "LEFT") {
it->second = false;
}
}
if (key == ALLEGRO_KEY_RIGHT) {
if (it->first == "RIGHT") {
it->second = false;
}
}
}
cout << it->first << " : " << it->second << endl;
}
}
void control_actor(Actor *target, int speed) {
if (key_states["UP"]) {
target->y -= speed;
}
if (key_states["DOWN"]) {
target->y += speed;
}
if (key_states["LEFT"]) {
target->x -= speed;
}
if (key_states["RIGHT"]) {
target->x += speed;
}
}
Actor player = {
"GFX\\player_up.png",
nullptr,
(SCREEN_WIDTH/2) - (ACTOR_SIZE/2),
(SCREEN_HEIGHT/2) - (ACTOR_SIZE/2),
8};
void init_game_bitmaps() {
player.bmp = load_bmp(player.path);
}
void draw_game_bitmaps() {
al_draw_bitmap(player.bmp, player.x, player.y, 0);
al_flip_display();
}
Теперь вот мой основной файл:
main.cpp
#include "init.h"
#include "game.h"
int main(int argc, char **argv){
init_all();
register_all();
init_game_bitmaps();
init_key_states();
while (running) {
draw_game_bitmaps();
al_wait_for_event(event_queue, &e);
if (e.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
running = false;
}
check_states();
control_actor(&player, player.speed);
if (e.type == ALLEGRO_EVENT_KEY_DOWN) {
if (e.keyboard.keycode == ALLEGRO_KEY_ESCAPE) {
running = false;
}
if (e.keyboard.keycode == ALLEGRO_KEY_ENTER) {
cout << "It works!";
}
}
}
destroy_all();
return 0;
}
Как вы можете видеть, у меня есть зЬй :: карта, которая хранит основные состояния (по одному для каждой стрелки на клавиатуре), а затем у меня есть процедура называется check_states(), которые перебирают все состояния в каждом основном цикле и устанавливают их в true, если их соответствующие стрелки нажаты (вниз), и к false при их освобождении.
Проблема:
Если я нажимаю UP и держать его держит, а потом нажать ВЛЕВО (не отпуская клавишу UP), игрок будет двигаться по диагонали. Тем не менее, если я отпустите LEFT, а все еще удерживая клавишу UP, игрок остановится, и состояние для UP будет истинным (И я вижу это, потому что я couting it).
Теория
The al_wait_for_event() ждет, пока очередь событие, указанное не является непустым (Это означает, что при нажатии на кнопку UP, он копирует событие е, а затем, когда я нажимаю влево , он должен отменить UP и назначить новое событие e, создав тем самым неприятную LAG, когда я нажимаю сразу несколько клавиш). Имея это в виду, я пришел к выводу: я мог бы иметь по крайней мере пять отдельных event_queues и пять разных «объектов событий». Мне удалось создать вектор event_queues и событий и перебрать их по каждому из них в каждом главном цикле, передав каждое событие в соответствующее событие event_queue И ПРОБЛЕМУ, ЗАЩИЩЕННУЮ.
SO, как я могу нажать более одной клавиши в в реальном времени? В реальном времени я имею в виду реальное в реальном времени, без задержек, без каких-либо ключей, отменяющих друг друга. Ответ - ключевые состояния? Зачем? Как я могу это сделать с помощью событий? Это вообще возможно?.
EDIT:
я изменил процедуру control_actor() для того, чтобы использовать al_key_down() вместо проверки событий, вот его код:
void control_actor(ALLEGRO_KEYBOARD_STATE *key, Actor *target, int speed) {
if (al_key_down(key, ALLEGRO_KEY_UP)) {
target->y -= speed;
cout << "UP" << endl;
}
if (al_key_down(key, ALLEGRO_KEY_DOWN)) {
target->y += speed;
cout << "DOWN" << endl;
}
if (al_key_down(key, ALLEGRO_KEY_LEFT)) {
target->x -= speed;
cout << "LEFT" << endl;
}
if (al_key_down(key, ALLEGRO_KEY_RIGHT)) {
target->x += speed;
cout << "RIGHT" << endl;
}
}
И новый главный ,каст:
#include "init.h"
#include "game.h"
int main(int argc, char **argv){
init_all();
register_all();
init_game_bitmaps();
ALLEGRO_KEYBOARD_STATE key;
while (running) {
draw_game_bitmaps();
al_wait_for_event(event_queue, &e);
al_get_keyboard_state(&key);
control_actor(&key, &player, player.speed);
if (e.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
running = false;
}
if (e.type == ALLEGRO_EVENT_KEY_DOWN) {
if (e.keyboard.keycode == ALLEGRO_KEY_ESCAPE) {
running = false;
}
if (e.keyboard.keycode == ALLEGRO_KEY_ENTER) {
cout << "It works!";
}
}
}
destroy_all();
return 0;
}
The post on the allegro forums linked in the comment is from 2002, и этот код больше не работает на Allegro 5. So I've checked the docs, и я вам скажу: ПРОБЛЕМА сохранялось. ТОЧНАЯ такая же вещь происходит. Одна стрелка отменяет другую, и игрок перестает двигаться некоторое время, как только я нажимаю еще одну стрелку одновременно.
Невозможно создать две переменные события, такие как 'e' и' f'. Затем вы можете опросить, чтобы увидеть, произошло ли событие 'e' нажатием клавиши, а затем посмотреть, произошло ли событие' f' нажатием клавиши. Похоже, что для этого потребуется много дел if-else. В противном случае [это] (https://www.allegro.cc/forums/thread/196299), похоже, касается вашей проблемы. – mas4
Это сообщение с 2002 года, и код больше не работает. Тем не менее, я изучил в документах, как использовать его таким образом, и я отредактировал сообщение с обновленной проблемой. –