Это проблема, которая преследует меня годами.
Вот мои файлы 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;
}
Как видите, у меня есть std::map, в котором хранятся состояния клавиш (по одному для каждой стрелки клавиатуры), а затем у меня есть процедура с именем check_states(), которая перебирает все состояния в каждом основном цикле и устанавливает их значение true, если соответствующие стрелки нажаты (вниз), и значение false, когда они отпущены.
Проблема:
Если я нажму ВВЕРХ и буду держать, а затем нажму ВЛЕВО (не отпуская клавишу ВВЕРХ), игрок будет двигаться по диагонали. Тем не менее, если я отпущу ЛЕВУЮ, все еще удерживая клавишу ВВЕРХ, проигрыватель остановится, и состояние для ВВЕРХ будет истинным (и я вижу это, потому что я m отмечая это).
Теория
al_wait_for_event() ожидает, пока указанная очередь событий не станет пустой (что означает, что когда Я нажимаю клавишу ВВЕРХ, она копирует событие в e, а затем, когда я нажимаю ВЛЕВО, она должна отменить ВВЕРХ и назначить новое событие на e, тем самым создавая неприятный LAG, когда я нажимаю более одной клавиши одновременно). Имея это в виду, я пришел к выводу: ну, я мог бы иметь как минимум ПЯТЬ отдельных event_queues и ПЯТЬ различных «объектов событий». Мне удалось создать вектор event_queues и событий и выполнить итерацию по ним обоим в каждом основном цикле, передавая каждое событие в соответствующую event_queue И ПРОБЛЕМА РЕШАЕТСЯ.
Итак, как я могу нажать более одной клавиши в реальном времени? Под реальным временем я подразумеваю реальное реальное время, без задержек, без каких-либо клавиш, отменяющих друг друга. Ответ: ключевые состояния? Почему? Как я могу сделать это с помощью событий? Это вообще возможно?.
ИЗМЕНИТЬ:
Я изменил свою процедуру 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;
}
}
И новый main.cpp:
#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;
}
Сообщение на форумах allegro, ссылка на которое содержится в комментарии, относится к 2002 году, и этот код не больше не работает на Allegro 5. Поэтому я проверил документацию и Я скажу вам: ПРОБЛЕМА РЕШАЕТСЯ. Происходит ТОЧНО то же самое. Одна стрелка отменяет другую и игрок перестает двигаться на какое-то время, как только я одновременно нажимаю другую стрелку.
e
иf
. Затем вы можете опросить, чтобы узнать, подхватило ли событиеe
нажатие клавиши, а затем посмотреть, подхватило ли событиеf
нажатие клавиши. Однако кажется, что для этого потребуется много случаев if-else. В противном случае это, похоже, решит вашу проблему. - person mas4   schedule 06.05.2015