BUAA-Soft-2023-Summer / Soft-Summer-2023

北航软件学院 2023 夏《程序设计实践》 小学期仓库
18 stars 0 forks source link

关于Visual Studio获取按键信息所出现的问题 #15

Closed guangmingzhengda closed 1 year ago

guangmingzhengda commented 1 year ago

目前所需要解决功能为获取k次用户按键,且必须为指定按键范围才进行下一步(此范围为数字),设计了如下程序,但是会直接死循环(应该是),无法获取新按键,根据设置断点调试 $tmp$ 始终为"None",从未获得过其他值 此为message.cpp

#include <easyx.h>
#include "../inc/Message.h"
#include"../inc/Globals.h"

static MouseMessage mouse;

int userKey[256];

void PeekMouseMessage()
{
    ExMessage msg;

    mouse.isUp = false;
    mouse.isDown = false;
    while (peekmessage(&msg, EX_MOUSE| EX_KEY))
    {
        if (msg.message == WM_MOUSEMOVE)
        {
            mouse.x = msg.x;
            mouse.y = msg.y;
        }
        else if (msg.message == WM_LBUTTONUP)
        {
            mouse.isUp = true;
            mouse.isDown = false;
        }
        else if (msg.message == WM_LBUTTONDOWN)
        {
            mouse.isUp = false;
            mouse.isDown = true;
        }
        else if(msg.message == WM_KEYDOWN) {
            if (!msg.prevdown) {
                userKey[msg.vkcode] = 1;
            }
        }
    }
}

bool IsKeyDown(int key)
{
    int tmp = userKey[key];
    return userKey[key]=0,tmp;
//    return (GetAsyncKeyState(key) & 0x8000);
}

std::wstring GetKey() {
    for (int i = 65; i <= 90; i++) {
        if (IsKeyDown(i)) {
            std::wstring s = L"";
            s += char(i);
            return s;
        }
    }
    for (int i = 48; i <= 57; i++) {
        if (IsKeyDown(i)) {
            std::wstring s = L"";
            s += char(i);
            return s;
        }
    }
    if (IsKeyDown(VK_RETURN)) return L"Enter";
    if (IsKeyDown(VK_SPACE)) return L"Space";
    if (IsKeyDown(VK_DELETE)) return L"Delete";
    if (IsKeyDown(VK_BACK)) return L"Backspace";
    if (IsKeyDown(VK_LSHIFT) || IsKeyDown(VK_RSHIFT)) return L"Shift";

    return L"None";
}

MouseMessage* GetMouseMessage()
{
    return &mouse;
}

此为调用部分程序:


std::wstring las = L"None";
int flag = 1;
void GameInterface::Update()
{
//    PutAlphaImage(nullptr, 0, 0, &background);
    GameObject* Now = GetGameObject(0);
    Now->Draw(0);
    Now = GetGameObject(1);
    Now->Draw(1);
    //std::wstring tmp = GetKey();
    //if (tmp == L"None") tmp = las;
    //else las = tmp;
    //QuickDraw(tmp.c_str(),0,150,1000,350,60,0);
    if (flag) {
 //     Now->Qipai(2, 1);
        flag = 0;
        int t = 1, k = 2;
        if (t == 0) {
            while (k--) {
                if (!Now->_shoupai.size()) break;
                Now->_shoupai.erase(Now->_shoupai.begin());
            }
        }
        else if (t == 1) {
            while (k > 0) {
                QuickDraw(L"请输入要弃置的牌的编号:", 200, 300, 800, 350, 20, 0);
                std::wstring tmp = GetKey();
                if (tmp == L"None") tmp = las;
                else las = tmp;
                if (tmp != L"None") {
                    Now->_shoupai.erase(Now->GetKapai(tmp[0] - '0', t));
                    k--;
                }
            }
        }
    }
    if (IsKeyDown(VK_ESCAPE)) {
        GetApplication()->ChangeInterface(L"Main");
    }
}

运行结果如图: QQ截图20230828223630 本身获取按键信息应该是没有问题的,采用如下代码,通过输出可以看到按键信息:

std::wstring las = L"None";
int flag = 1;
void GameInterface::Update()
{
//    PutAlphaImage(nullptr, 0, 0, &background);
    GameObject* Now = GetGameObject(0);
    Now->Draw(0);
    Now = GetGameObject(1);
    Now->Draw(1);
    std::wstring tmp = GetKey();
    if (tmp == L"None") tmp = las;
    else las = tmp;
    tmp = L"按下了" + tmp;
    QuickDraw(tmp.c_str(),0,150,1000,350,60,0);
    if (IsKeyDown(VK_ESCAPE)) {
        GetApplication()->ChangeInterface(L"Main");
    }
}

结果如图: QQ截图20230828223902 因此我推测可能是判断逻辑写错,或者是清空释放有问题,还望大佬能给出解答!

Lord-Turmoil commented 1 year ago

首先,你这个写法有一定的隐患,会导致同一次循环中无法两次检测某一按键。更好的做法是在 PeekMouseMessage 函数开始时清空 userKey 数组。

return userKey[key] = 0, tmp;
Lord-Turmoil commented 1 year ago

最好能提供完整的项目,不然问题不好复现。

guangmingzhengda commented 1 year ago

最好能提供完整的项目,不然问题不好复现。

按学长所提供对于message.cpp的修改进行了修改,但是目前还是同样的问题

GameInterface.cpp中,若取消注释$90\sim 94$行,可以发现是可以正常检测键位,若取消$103\sim129$,则会出现类似于死循环的现象没有任何输出,现附上所有代码,烦请学长拨冗帮忙看一下! Sanguosha.zip (因为包含少量图片,整体偏大,见谅!)

Lord-Turmoil commented 1 year ago

你的问题主要有以下三点。

1. 死循环问题

在原始代码的 114 - 126 行的 while 循环判断 k 的值,但是游戏刚开始时,玩家来不及按键,因此会陷入循环,而循环中又没有接收新按键消息的函数,因此无法跳出循环,导致死循环。

2. 没有输出的问题

因为你使用了 BatchDraw,因此程序只会在 FlushBatchDraw() 时将所有内容一并绘制在屏幕上,而 114 - 126 这个循环并没有使用这一函数,因此不会有任何输出,屏幕始终停留在上一帧。

3. 获取按键消息的函数实现非常不妥

IsKeyDown() 本应是一个没有副作用(只读取值,而不改变值)的函数,但是你在这里面对按键信息进行了清除。虽然可能实现了你希望的功能,但是有一定隐患。

guangmingzhengda commented 1 year ago

谢谢学长指导!根据学长的修改意见,已经对IsKeyDown函数进行了修改,在while循环中增加了接收按键消息的函数,目前可以正常获得键值,但还会存在之前所提到的一个按键无法释放的问题(即系统一直认为该按键按住),应该还是Message.cpp中的问题,我再研究一下,实在不行明天再去找学长,谢谢!

guangmingzhengda commented 1 year ago

谢谢学长,如何清空释放的问题我已经解决,剩下的问题应该就和此前关系不大了,再次感谢学长!