当前位置: 首页 > news >正文

C++ 鸭科夫手柄适配

使用Win项目编译

//Ctrl.h
#pragma once
#include <Windows.h>
#include <Xinput.h>
#include <iostream>
#include <thread>
#include <atomic>
#define TRIGGER_THRESHOLD  30
#define RIGHT_THUMB_DEADZONE 8689
#define SMOOTHING_FACTOR  0.7f
#define MOUSE_SENSITIVITY 0.2f
#pragma comment(lib, "Xinput.lib")
#pragma comment(lib, "User32.lib")class GamepadMapper {
private:std::atomic<bool> running;// 死区阈值,避免摇杆轻微漂移const int LEFT_THUMB_DEADZONE = 7849;  // XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE// 按键状态跟踪bool lastjiaState = false;bool lastjianState = false;bool lastBState = false;bool lastYState = false;bool lastAState = false;bool lastRTState = false;bool lastLTState = false;bool lastRSButtonState = false;bool lastLSButtonState = false;bool lastXState = false;bool LState = false;bool RState = false;bool UState = false;bool DState = false;POINT screenCenter;int max_mm = 0;int now_max_mm = 0;
public:GamepadMapper() : running(true) {int screenWidth = GetSystemMetrics(SM_CXSCREEN);int screenHeight = GetSystemMetrics(SM_CYSCREEN);screenCenter.x = screenWidth / 2;screenCenter.y = screenHeight / 2 - (screenHeight / 20);max_mm = (screenHeight / 2) - (screenHeight / 8);now_max_mm = max_mm;}~GamepadMapper() {stop();}void stop() {running = false;}// 模拟键盘按键void simulateKey(WORD key, bool press) {INPUT input = { 0 };input.type = INPUT_KEYBOARD;// 尝试获取扫描码UINT scanCode = MapVirtualKey(key, MAPVK_VK_TO_VSC);if (scanCode != 0) {input.ki.wScan = scanCode;input.ki.dwFlags = KEYEVENTF_SCANCODE | (press ? 0 : KEYEVENTF_KEYUP);}else {// 回退到虚拟键码input.ki.wVk = key;input.ki.wScan = 0;input.ki.dwFlags = press ? 0 : KEYEVENTF_KEYUP;}input.ki.time = 0;input.ki.dwExtraInfo = 0;SendInput(1, &input, sizeof(INPUT));}// 模拟鼠标按键void simulateMouseClick(DWORD flags, bool press) {INPUT input = { 0 };input.type = INPUT_MOUSE;input.mi.dwFlags = press ? flags : (flags == MOUSEEVENTF_RIGHTDOWN ? MOUSEEVENTF_RIGHTUP : MOUSEEVENTF_LEFTUP);input.mi.time = 0;input.mi.dwExtraInfo = 0;SendInput(1, &input, sizeof(INPUT));}float smoothX = 0.0f;float smoothY = 0.0f;void processRightThumbstickForMouse_Line(SHORT x, SHORT y) {// 应用死区if (abs(x) < RIGHT_THUMB_DEADZONE) x = 0;if (abs(y) < RIGHT_THUMB_DEADZONE) y = 0;// 如果摇杆在死区内,重置平滑值if (x == 0 && y == 0) {smoothX = 0.0f;smoothY = 0.0f;return;}// 将摇杆值归一化到 [-1, 1] 范围float normalizedX = static_cast<float>(x) / 32767.0f;float normalizedY = static_cast<float>(y) / 32767.0f;// 应用平滑滤波smoothX = SMOOTHING_FACTOR * smoothX + (1.0f - SMOOTHING_FACTOR) * normalizedX;smoothY = SMOOTHING_FACTOR * smoothY - (1.0f - SMOOTHING_FACTOR) * normalizedY;// 计算鼠标移动距离(应用灵敏度)int mouseMoveX = static_cast<int>(smoothX * MOUSE_SENSITIVITY * 100);int mouseMoveY = static_cast<int>(smoothY * MOUSE_SENSITIVITY * 100);// 移动鼠标if (mouseMoveX != 0 || mouseMoveY != 0) {INPUT input = { 0 };input.type = INPUT_MOUSE;input.mi.dx = mouseMoveX;input.mi.dy = mouseMoveY;input.mi.dwFlags = MOUSEEVENTF_MOVE;input.mi.time = 0;input.mi.dwExtraInfo = 0;SendInput(1, &input, sizeof(INPUT));}}bool lock_mouse = true;void processRightThumbstickForMouse(SHORT x, SHORT y) {// 应用死区if (abs(x) < RIGHT_THUMB_DEADZONE) x = 0;if (abs(y) < RIGHT_THUMB_DEADZONE) y = 0;// 如果摇杆在死区内,不移动if (x == 0 && y == 0) {return;}// 将摇杆值归一化到 [-1, 1] 范围float normalizedX = static_cast<float>(x) / 32767.0f;float normalizedY = static_cast<float>(y) / 32767.0f;// 计算摇杆输入对应的角度float inputAngle = atan2(normalizedY, normalizedX);// 计算圆形轨迹上的目标位置int targetX = screenCenter.x + static_cast<int>(now_max_mm * cos(inputAngle));int targetY = screenCenter.y - static_cast<int>(now_max_mm * sin(inputAngle));// 获取当前鼠标位置POINT currentPos;GetCursorPos(&currentPos);// 计算需要移动的相对距离int deltaX = targetX - currentPos.x;int deltaY = targetY - currentPos.y;// 应用插值平滑 - 使用缓动函数让移动更柔和const float SMOOTH_FACTOR = 0.1f; // 调整这个值来控制平滑程度 (0.1-0.5)// 计算平滑后的移动距离int smoothDeltaX = static_cast<int>(deltaX * SMOOTH_FACTOR);int smoothDeltaY = static_cast<int>(deltaY * SMOOTH_FACTOR);// 确保至少移动1像素,避免微小移动被忽略if (abs(smoothDeltaX) < 1 && abs(deltaX) > 0) {smoothDeltaX = (deltaX > 0) ? 1 : -1;}if (abs(smoothDeltaY) < 1 && abs(deltaY) > 0) {smoothDeltaY = (deltaY > 0) ? 1 : -1;}// 移动鼠标if (smoothDeltaX != 0 || smoothDeltaY != 0) {INPUT input = { 0 };input.type = INPUT_MOUSE;input.mi.dx = smoothDeltaX;input.mi.dy = smoothDeltaY;input.mi.dwFlags = MOUSEEVENTF_MOVE;input.mi.time = 0;input.mi.dwExtraInfo = 0;SendInput(1, &input, sizeof(INPUT));}}// 处理左摇杆输入void processLeftThumbstick(SHORT x, SHORT y) {// 应用死区if (abs(x) < LEFT_THUMB_DEADZONE) x = 0;if (abs(y) < LEFT_THUMB_DEADZONE) y = 0;// 映射到WASDbool sPressed = (y < -LEFT_THUMB_DEADZONE);bool wPressed = (y > LEFT_THUMB_DEADZONE);bool aPressed = (x < -LEFT_THUMB_DEADZONE);bool dPressed = (x > LEFT_THUMB_DEADZONE);// 发送按键事件static bool lastW = false, lastA = false, lastS = false, lastD = false;if (wPressed != lastW) {simulateKey('W', wPressed);lastW = wPressed;}if (sPressed != lastS) {simulateKey('S', sPressed);lastS = sPressed;}if (aPressed != lastA) {simulateKey('A', aPressed);lastA = aPressed;}if (dPressed != lastD) {simulateKey('D', dPressed);lastD = dPressed;}}void processTriggers(const XINPUT_GAMEPAD& gamepad) {bool rtPressed = (gamepad.bRightTrigger > TRIGGER_THRESHOLD);if (rtPressed != lastRTState) {simulateMouseClick(MOUSEEVENTF_LEFTDOWN, rtPressed);lastRTState = rtPressed;}rtPressed = (gamepad.bLeftTrigger > TRIGGER_THRESHOLD);if (rtPressed != lastLTState) {simulateMouseClick(MOUSEEVENTF_RIGHTDOWN, rtPressed);lastLTState = rtPressed;}// 如果需要,也可以处理LT键// bool ltPressed = (gamepad.bLeftTrigger > TRIGGER_THRESHOLD);// 处理LT键映射...}// 处理按钮输入WORD now_number = 0x31;void processButtons(const XINPUT_GAMEPAD& gamepad) {// B键 -> Shiftbool bPressed = (gamepad.wButtons & XINPUT_GAMEPAD_B);if (bPressed != lastBState) {simulateKey(VK_LSHIFT, bPressed);lastBState = bPressed;}bPressed = (gamepad.wButtons & XINPUT_GAMEPAD_START);if (bPressed != lastjiaState) {simulateKey(VK_TAB, bPressed);lastjiaState = bPressed;}bPressed = (gamepad.wButtons & XINPUT_GAMEPAD_BACK);if (bPressed != lastjianState) {simulateKey(0x4D, bPressed);lastjianState = bPressed;}bPressed = (gamepad.wButtons & XINPUT_GAMEPAD_X);if (bPressed != lastXState) {simulateKey(VK_SPACE, bPressed);lastXState = bPressed;}bPressed = (gamepad.wButtons & XINPUT_GAMEPAD_Y);if (bPressed != lastYState) {now_number = 0x31;simulateKey(0x56, bPressed);lastYState = bPressed;}bPressed = (gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER);if (bPressed != lastLSButtonState) {simulateKey(0x52, bPressed);lastLSButtonState = bPressed;}bPressed = (gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT);if (bPressed != LState) {if (bPressed){now_number--;if (now_number < 0x31)now_number = 0x31;}simulateKey(now_number, bPressed);LState = bPressed;}bPressed = (gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT);if (bPressed != RState) {if (bPressed){now_number++;if (now_number > 0x38)now_number = 0x38;}simulateKey(now_number, bPressed);RState = bPressed;}bPressed = (gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP);if (bPressed != UState) {//simulateKey(0x59, bPressed);if (bPressed){now_max_mm += 3;if (now_max_mm > max_mm)now_max_mm = max_mm;}UState = bPressed;}bPressed = (gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN);if (bPressed != UState) {//simulateKey(0x43, bPressed);if (bPressed){now_max_mm -= 3;if (now_max_mm < 40)now_max_mm = 40;}UState = bPressed;}// Y键 -> 鼠标右键//bool yPressed = (gamepad.wButtons & XINPUT_GAMEPAD_Y);//if (yPressed != lastYState) {//    //simulateMouseClick(MOUSEEVENTF_RIGHTDOWN, yPressed);//    simulateMouseClick(MOUSEEVENTF_LEFTDOWN, yPressed);//    lastYState = yPressed;//}bool rsPressed = (gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER);if (rsPressed != lastRSButtonState) {if (rsPressed){lock_mouse = !lock_mouse;if (lock_mouse)Beep(1000, 200);elseBeep(800, 300);}lastRSButtonState = rsPressed;}// A键 -> F键bool aPressed = (gamepad.wButtons & XINPUT_GAMEPAD_A);if (aPressed != lastAState) {simulateKey('F', aPressed);lastAState = aPressed;}}void run() {while (running) {for (DWORD i = 0; i < XUSER_MAX_COUNT && running; i++) {XINPUT_STATE state;ZeroMemory(&state, sizeof(XINPUT_STATE));DWORD result = XInputGetState(i, &state);if (result == ERROR_SUCCESS) {// 处理左摇杆processLeftThumbstick(state.Gamepad.sThumbLX, state.Gamepad.sThumbLY);// 处理按钮processButtons(state.Gamepad);processTriggers(state.Gamepad);if (lock_mouse)processRightThumbstickForMouse(state.Gamepad.sThumbRX,state.Gamepad.sThumbRY);elseprocessRightThumbstickForMouse_Line(state.Gamepad.sThumbRX,state.Gamepad.sThumbRY);}}// 降低CPU使用率std::this_thread::sleep_for(std::chrono::milliseconds(10));}// 释放所有按键releaseAllKeys();}private:void releaseAllKeys() {// 释放所有可能按下的按键simulateKey('W', false);simulateKey('A', false);simulateKey('S', false);simulateKey('D', false);simulateKey(VK_LSHIFT, false);simulateKey(VK_SPACE, false);simulateKey('F', false);simulateKey(0x52, false);simulateKey(0x56, false);simulateKey(0x59, false);simulateKey(0x43, false);simulateMouseClick(MOUSEEVENTF_RIGHTDOWN, false);simulateMouseClick(MOUSEEVENTF_LEFTDOWN, false);}
};// 控制台事件处理
BOOL WINAPI ConsoleHandler(DWORD signal) {if (signal == CTRL_C_EVENT) {std::cout << "\n收到退出信号,程序即将退出..." << std::endl;return TRUE;}return FALSE;
}
//主函数
#include <windows.h>
#include <string>
#include"resource.h"
#include"Ctrl.h"// 窗口过程函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);GamepadMapper mapper;
std::thread* mapperThread;
void runMapper(GamepadMapper& mapper) {mapper.run();
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{// 窗口类名const wchar_t CLASS_NAME[] = L"Sample Window Class";// 注册窗口类WNDCLASS wc = {};wc.lpfnWndProc = WindowProc;wc.hInstance = hInstance;wc.lpszClassName = CLASS_NAME;wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);wc.hCursor = LoadCursor(NULL, IDC_ARROW);wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));RegisterClass(&wc);// 计算窗口大小(包含标题栏和边框)RECT rect = { 0, 0, 420, 400 };AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);int width = rect.right - rect.left;int height = rect.bottom - rect.top;// 创建窗口HWND hwnd = CreateWindowEx(0,                              // 扩展窗口样式CLASS_NAME,                     // 窗口类L"逃离鸭科夫-手柄映射",          // 窗口标题WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX & ~WS_SIZEBOX, // 窗口样式:去掉最大化和调整大小CW_USEDEFAULT, CW_USEDEFAULT,   // 位置width, height,                  // 大小NULL,                           // 父窗口NULL,                           // 菜单hInstance,                      // 实例句柄NULL                            // 附加数据);if (hwnd == NULL){return 0;}// 显示窗口ShowWindow(hwnd, nCmdShow);UpdateWindow(hwnd);// 在单独线程中运行映射器std::thread mapperThread1(runMapper, std::ref(mapper));mapperThread = &mapperThread1;// 消息循环MSG msg = {};while (GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return 0;
}// 窗口过程函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{switch (uMsg){case WM_PAINT:{PAINTSTRUCT ps;HDC hdc = BeginPaint(hwnd, &ps);// 获取客户区大小RECT clientRect;GetClientRect(hwnd, &clientRect);// 设置文本颜色和背景模式SetTextColor(hdc, RGB(0, 0, 0));  // 黑色文字SetBkMode(hdc, TRANSPARENT);      // 透明背景// 创建字体HFONT hFont = CreateFont(24,                           // 字体高度0,                            // 字体宽度0,                            // 文本倾斜度0,                            // 字体倾斜度FW_NORMAL,                    // 字体粗细FALSE,                        // 斜体FALSE,                        // 下划线FALSE,                        // 删除线DEFAULT_CHARSET,              // 字符集OUT_DEFAULT_PRECIS,           // 输出精度CLIP_DEFAULT_PRECIS,          // 裁剪精度DEFAULT_QUALITY,              // 输出质量DEFAULT_PITCH | FF_DONTCARE,  // 字体系列L"Arial"                      // 字体名称);// 选择字体到设备上下文HFONT hOldFont = (HFONT)SelectObject(hdc, hFont);// 要显示的文本 - 可以包含换行符const wchar_t* text = L"左摇杆       -> 控制移动\r\n右摇杆       -> 控制射击方向\r\nB键          -> 奔跑\r\nY键          -> 近战武器\r\nA键          -> 交互\r\nX键          -> 翻滚\r\n减号         -> 地图(再按一次关闭)\r\n加号         -> 背包(再按一次关闭)\r\n左键         -> 数字相减(切换武器)\r\n右键         -> 数字相加(切换武器)\r\n左扳机键(LT) -> 瞄准\r\n右扳机键(RT) -> 开火\r\n左长条键(LB) -> 换弹\r\n右长条键(RB) -> 切换准心模式";// 计算文本矩形(留出一些边距)RECT textRect = clientRect;textRect.left += 20;textRect.right -= 20;textRect.top += 20;textRect.bottom -= 20;// 使用DrawText绘制文本,支持自动换行DrawText(hdc,text,-1,  // 自动计算文本长度&textRect,DT_VCENTER |    // 垂直居中DT_WORDBREAK |  // 自动换行DT_NOPREFIX);   // 不处理&字符// 恢复原来的字体并删除新字体SelectObject(hdc, hOldFont);DeleteObject(hFont);EndPaint(hwnd, &ps);}return 0;case WM_DESTROY:mapper.stop();mapperThread->join();PostQuitMessage(0);return 0;case WM_GETMINMAXINFO:{// 限制窗口最小大小,使其不能调整大小LPMINMAXINFO lpMMI = (LPMINMAXINFO)lParam;RECT rect = { 0, 0, 420, 400 };AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX & ~WS_SIZEBOX, FALSE);lpMMI->ptMinTrackSize.x = rect.right - rect.left;lpMMI->ptMinTrackSize.y = rect.bottom - rect.top;lpMMI->ptMaxTrackSize.x = rect.right - rect.left;lpMMI->ptMaxTrackSize.y = rect.bottom - rect.top;}return 0;}return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
http://www.dtcms.com/a/545211.html

相关文章:

  • dubbo和springcloud的差别
  • Linux系统编程——目录操作函数
  • MitoSOX Red 别名:Mitochondrial Superoxide Indicator; 红色线粒体超氧化物荧光探针
  • 深圳做网站做得比较好的公司struts2 做的网站
  • Reflex:用纯Python写交互式Web应用,从0到1构建你的第一个UI
  • 怎么查找网站是谁做的钢材网站模板
  • 打造高清3D虚拟世界|零基础学习Unity HDRP高清渲染管线(第十天)
  • oto电子商务网站建设网站建设的公司上海
  • 【数据结构】链式结构二叉树详解
  • Flutter兼容性问题:Could not get unknown property ‘flutter‘ for extension ‘android‘
  • 【Linux系统编程】自动化构建-make/Makefile
  • php网站打开慢青海高端网站建设
  • 仓颉TreeMap红黑树结构深度解析
  • React中Suspense的分包实践
  • 垃圾收集器
  • CSharp UI更新及跨线程更新总结
  • 两个域名同一个网站做优化淘宝优惠网站怎么做
  • 深入仓颉UI:事件处理的声明式哲学与高阶实践
  • Actix Web 入门与实战
  • 外贸soho建站云南省建设厅网站二建
  • 20251029在AIO-3576Q38开发板的Android14下使用iperf3测试WIFI模块AP6256的网速【87.8 Mbits/sec】
  • 怎么用dede建设网站网站建设开放的端口
  • 基本select语句
  • linux命令-系统信息与监控-2
  • 【Ubuntu】安装amd驱动及ROCM后,系统起不来的问题分析及解决方案
  • 外国网站后台设计iis网页提示网站建设中
  • 镇江网站建设多少钱北京seo网站优化公司
  • 【第一章】金融数据的获取——金融量化学习入门笔记
  • MoonBit Pearls Vol.13:使用 MoonBit 开发一个 HTTP 文件服务器
  • 网站建设如何描述htm网站制作