You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++实现游戏角色按时长依次长按W/A/S/D按键功能求助

问题描述

刚接触C++,想实现游戏角色在任意游戏中按指定时长依次长按按键:先长按W键2秒,接着长按A键2秒,再长按S键2秒,最后长按D键2秒。但自己写的代码有卡顿问题,而且对多数代码内容不理解,附上代码求助:

int sleepBetween = 50;

if (GetAsyncKeyState(VK_NUMPAD1))
{
    std::cout << "Working..." << endl;

    SHORT key;
    UINT mappedkey;

    // 循环
    while (true)
    {
        INPUT input = {};
        key = VkKeyScan('w');

        mappedkey = MapVirtualKey(LOBYTE(key), 0);
        input.type = INPUT_KEYBOARD;
        input.ki.dwFlags = KEYEVENTF_SCANCODE;
        input.ki.wScan = mappedkey;

        SendInput(1, &input, sizeof(INPUT));
        Sleep(10);

        input.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP;
        SendInput(1, &input, sizeof(INPUT));

        Sleep(sleepBetween);

        key = VkKeyScan('a');

        mappedkey = MapVirtualKey(LOBYTE(key), 0);
        input.type = INPUT_KEYBOARD;
        input.ki.dwFlags = KEYEVENTF_SCANCODE;
        input.ki.wScan = mappedkey;

        SendInput(1, &input, sizeof(INPUT));
        Sleep(10);

        input.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP;
        SendInput(1, &input, sizeof(INPUT));

        Sleep(sleepBetween);

        key = VkKeyScan('s');

        mappedkey = MapVirtualKey(LOBYTE(key), 0);
        input.type = INPUT_KEYBOARD;
        input.ki.dwFlags = KEYEVENTF_SCANCODE;
        input.ki.wScan = mappedkey;

        SendInput(1, &input, sizeof(INPUT));
        Sleep(10);

        input.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP;
        SendInput(1, &input, sizeof(INPUT));

        Sleep(sleepBetween);

        Sleep(sleepBetween);

        key = VkKeyScan('d');

        mappedkey = MapVirtualKey(LOBYTE(key), 0);
        input.type = INPUT_KEYBOARD;
        input.ki.dwFlags = KEYEVENTF_SCANCODE;
        input.ki.wScan = mappedkey;

        SendInput(1, &input, sizeof(INPUT));
        Sleep(10);

        input.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP;
        SendInput(1, &input, sizeof(INPUT));

        Sleep(sleepBetween);

        // 按小键盘2停止
        if (GetAsyncKeyState(VK_NUMPAD2))
        {
            std::cout << "Stopped!" << endl;
            break;
        }
    }
}
问题分析与改进方案

1. 原代码核心问题

  • 长按逻辑错误:原代码按下按键后仅Sleep(10ms)就松开,实际是点按而非长按,完全不符合2秒长按的需求
  • 代码冗余:重复编写4次几乎一致的按键操作,维护难度高
  • 卡顿原因:主循环中的Sleep会阻塞线程,且未处理停止信号的实时检测;同时原代码的时间间隔设置和需求完全不符
  • 按键检测不严谨:GetAsyncKeyState未判断最高位(0x8000),可能导致误触发

2. 改进后的代码实现

#include <windows.h>
#include <iostream>

// 通用长按按键函数:传入扫描码和长按时长(毫秒)
void HoldKey(UINT scancode, DWORD duration_ms) {
    INPUT input = {0};
    input.type = INPUT_KEYBOARD;
    input.ki.dwFlags = KEYEVENTF_SCANCODE;
    input.ki.wScan = scancode;

    // 发送按下按键指令
    SendInput(1, &input, sizeof(INPUT));

    // 长按过程中实时检测停止键,避免完全阻塞
    DWORD start_time = GetTickCount();
    while (GetTickCount() - start_time < duration_ms) {
        if (GetAsyncKeyState(VK_NUMPAD2) & 0x8000) {
            // 先松开当前按键再退出
            input.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP;
            SendInput(1, &input, sizeof(INPUT));
            std::cout << "Stopped during hold!" << std::endl;
            exit(0);
        }
        Sleep(10); // 降低CPU占用
    }

    // 发送松开按键指令
    input.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP;
    SendInput(1, &input, sizeof(INPUT));
}

int main() {
    std::cout << "按小键盘1开始,小键盘2停止" << std::endl;

    while (true) {
        // 检测开始键:判断最高位确认按键当前按下
        if (GetAsyncKeyState(VK_NUMPAD1) & 0x8000) {
            std::cout << "Working..." << std::endl;

            // 依次长按W/A/S/D各2秒(扫描码为硬件固定值,游戏兼容性更好)
            HoldKey(0x11, 2000); // W键
            HoldKey(0x1E, 2000); // A键
            HoldKey(0x1F, 2000); // S键
            HoldKey(0x20, 2000); // D键

            std::cout << "一轮完成,等待再次触发..." << std::endl;
            // 等待松开开始键,防止重复触发
            while (GetAsyncKeyState(VK_NUMPAD1) & 0x8000) {
                Sleep(10);
            }
        }

        // 全局检测停止键
        if (GetAsyncKeyState(VK_NUMPAD2) & 0x8000) {
            std::cout << "Stopped!" << std::endl;
            break;
        }

        Sleep(10); // 降低CPU占用
    }

    return 0;
}

3. 关键代码解释

  • 扫描码(scancode):游戏通常优先响应硬件扫描码,比虚拟键兼容性更好,W/A/S/D的扫描码为固定值(0x11/0x1E/0x1F/0x20)
  • HoldKey函数:封装了按键按下、保持、松开的完整逻辑,同时在长按过程中实时检测停止信号,避免程序完全阻塞
  • GetAsyncKeyState正确用法:通过按位与0x8000判断按键当前是否处于按下状态,避免误触发
  • 卡顿优化:用小间隔Sleep+循环检测的方式,既保证长按时长,又能实时响应停止指令,同时降低CPU占用

内容的提问来源于stack exchange,提问作者Martin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 14:50:37