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

时间:2015-05-06 03:31:56

标签: c++ events allegro allegro5

这是困扰我多年的问题。

这是我的 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。

问题:

如果我按住UP并保持按住,然后我按下LEFT(不释放UP键),播放器将沿对角线方向移动。 然而,如果我释放LEFT,而仍然按住UP键,播放器将停止,UP的状态将为真(我看到这是因为我' m couting 它。)

理论

al_wait_for_event()等待,直到指定的事件队列为非空(这意味着当我按下UP键时,它会将事件复制到 e ,然后当我按下LEFT,它必须取消UP并为 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;
}

The post on the allegro forums linked in the comment is from 2002,该代码在Allegro 5上不再有效。So I've checked the docs,我会告诉您:问题持续存在。完全相同的事情发生了。一个箭头取消另一个箭头,一旦我同时按下另一个箭头,玩家就会停止移动一段时间。

1 个答案:

答案 0 :(得分:2)

快板维基上的Basic Keyboard Example可能比旧帖更有帮助。

此处无需管理多个事件队列。每个按键和释放键都应该被推入队列 - 您只需要确保处理队列中的每个事件。

基本上,你需要一个主循环:

  1. 处理当前在队列中的每个事件
  2. 更新游戏状态
  3. 重绘屏幕
  4. 这是我起草的一个例子:

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

    希望这有帮助!