如何实时使用事件同时按多个键?(Allegro 5)

How to press multiple keys at the same time using events in real-time? (Allegro 5)

本文关键字:Allegro 事件 何实时 实时      更新时间:2023-10-16

这是一个困扰我多年的问题。

这是我的 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 ::映射存储键状态(一个用于键盘的每个箭头),然后我有一个称为check_states()的过程,该过程在每个主循环处的所有状态都迭代,如果将它们各自的箭头按下(向下),并在释放它们时将它们设置为true。

问题:

如果我按向上并保持固定,然后按向左(不释放向上键),则玩家将对角线移动。尽管如此m couting it)。

理论

al_wait_for_event()等到指定的事件队列是非空的(这意味着当我按下键时,它将事件复制到 e ,然后当我按左键时,它必须取消并将新事件分配给 e ,从而在我一次按多个键时创建不愉快的滞后)。考虑到这一点,我得出结论:嗯,我至少可以拥有五个单独的event_queues和五个不同的"事件对象"。我设法创建了一个事件_queues和Event的向量,并在每个主循环中迭代两个事件,同时将每个事件传递给其各自的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上不再起作用。/strong>。完全相同的事情也发生了。一个箭会取消另一支箭头,玩家停止移动一段时间,一旦我同时按另一箭头。

Allegro Wiki上的基本键盘示例可能比该旧帖子更有帮助。

无需在此处管理多个事件队列。每个键的按键和发布都应将其推入队列 - 您只需要确保处理队列中的每个事件。

基本上,您想要一个主要循环:

  1. processes 当前队列中的每个事件
  2. 更新游戏状态
  3. redraws屏幕

这是我起草的示例:

#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,它将继续向右移动,但不会向下移动。

希望这会有所帮助!