2015-05-06 5 views
0

Это проблема, которая преследует меня в течение многих лет.Как нажимать несколько клавиш одновременно с использованием событий в режиме реального времени? (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, и я вам скажу: ПРОБЛЕМА сохранялось. ТОЧНАЯ такая же вещь происходит. Одна стрелка отменяет другую, и игрок перестает двигаться некоторое время, как только я нажимаю еще одну стрелку одновременно.

+1

Невозможно создать две переменные события, такие как 'e' и' f'. Затем вы можете опросить, чтобы увидеть, произошло ли событие 'e' нажатием клавиши, а затем посмотреть, произошло ли событие' f' нажатием клавиши. Похоже, что для этого потребуется много дел if-else. В противном случае [это] (https://www.allegro.cc/forums/thread/196299), похоже, касается вашей проблемы. – mas4

+0

Это сообщение с 2002 года, и код больше не работает. Тем не менее, я изучил в документах, как использовать его таким образом, и я отредактировал сообщение с обновленной проблемой. –

ответ

2

Basic Keyboard Example на вики-лиге может быть более полезной, чем эта старая должность.

Нет необходимости управлять несколькими очередями событий здесь. Каждое нажатие и отключение клавиш должно попасть в очередь - вам просто нужно убедиться, что вы обрабатываете каждое событие в очереди.

В принципе, вы хотите, основной цикл, который:

  1. Процессы каждый событий в настоящее время в очереди
  2. Обновления состояние игры
  3. перерисовывает экран

Вот пример Я подготовил:

#include <stdio.h> 
#include <allegro5/allegro.h> 
#include <allegro5/allegro_primitives.h> 

const int SPEED = 5; 
const float FPS = 60; 

int main(int argc, char **argv) { 
    ALLEGRO_DISPLAY *display = NULL; 
    ALLEGRO_EVENT_QUEUE *event_queue = NULL; 
    ALLEGRO_TIMER *timer = NULL; 
    int x = 0, y = 0; // position 
    int vx = 0, vy = 0; // velocity 

    // initialize everything we need -- error checking omitted for brevity 
    al_init(); 
    al_install_keyboard(); 
    al_init_primitives_addon(); 
    display = al_create_display(640, 480); 
    event_queue = al_create_event_queue(); 
    timer = al_create_timer(1.0/FPS); 
    al_register_event_source(event_queue, al_get_keyboard_event_source()); 
    al_register_event_source(event_queue, al_get_timer_event_source(timer)); 
    al_start_timer(timer); 

    bool done = false; 
    while(!done) { 
    bool redraw = false; 

    // process events until queue is empty 
    while(!al_is_event_queue_empty(event_queue)) { 
     ALLEGRO_EVENT ev; 
     al_wait_for_event(event_queue, &ev); 

     switch(ev.type) { 
     case ALLEGRO_EVENT_KEY_DOWN: 
      switch(ev.keyboard.keycode) { 
      case ALLEGRO_KEY_W: 
       vy -= SPEED; // add upward velocity 
       break; 
      case ALLEGRO_KEY_S: 
       vy += SPEED; // add downward velocity 
       break; 
      case ALLEGRO_KEY_A: 
       vx -= SPEED; // add leftward velocity 
       break; 
      case ALLEGRO_KEY_D: 
       vx += SPEED; // add leftward velocity 
       break; 
      case ALLEGRO_KEY_ESCAPE: 
       done = true; 
       break; 
      } 
      break; 
     case ALLEGRO_EVENT_KEY_UP: 
      switch(ev.keyboard.keycode) { 
      case ALLEGRO_KEY_W: 
       vy += SPEED; // remove upward velocity 
       break; 
      case ALLEGRO_KEY_S: 
       vy -= SPEED; // remove downward velocity 
       break; 
      case ALLEGRO_KEY_A: 
       vx += SPEED; // remove leftward velocity 
       break; 
      case ALLEGRO_KEY_D: 
       vx -= SPEED; // remove leftward velocity 
       break; 
      } 
      break; 
     case ALLEGRO_EVENT_TIMER: 
      redraw = true; // time for next frame 
      break; 
     } 
    } 

    // got through all the events this loop -- redraw if necessary 
    if (redraw) { 
     // move circle 
     x += vx; 
     y += vy; 

     // draw circle 
     al_clear_to_color(al_map_rgb(0, 0, 0)); 
     al_draw_filled_circle(x, y, 20, al_map_rgb(0, 0, 255)); 
     al_flip_display(); 
    } 
    } 

    al_destroy_display(display); 

    return 0; 
} 

Примечание while(!al_is_event_queue_empty(event_queue)). Это гарантирует, что мы не пропустим какое-либо событие, прежде чем переходить к циклу обновления.

Если вы попытаетесь запустить пример, круг должен соответствующим образом отреагировать на любую комбинацию ключей WASD. Если вы удерживаете S + D, он будет двигаться по диагонали вправо и вниз. Выпуск S, и он продолжит движение вправо, но не вниз.

Надеюсь, это поможет!

+0

Теперь я понимаю, что я должен использовать таймер и перерисовать в ALLEGRO_EVENT_TIMER. Большое спасибо! Кстати, мне пришлось немного настроить ваш код, потому что мне не нужна скорость в моем случае. Я имею в виду, что ваш код будет идеально подходит для игры «Астероиды»: стр. –