AlexZ33 / lessions

自己练习的各种demo和课程
12 stars 2 forks source link

【手柄操作】gamepad api #126

Open AlexZ33 opened 1 year ago

AlexZ33 commented 1 year ago

let AXIS_LXLY = 2007, //左摇杆标识
    AXIS_RXRY = 2008, //右摇杆标识
    AXIS_LT = 2009, //LT杆标识
    AXIS_RT = 2010, //RT杆标识
    KEY_DOWN = 2011, // 按键按下
    KEY_UP = 2012; // 按键抬起
let gamepadconnected,
    gamepaddisconnected;

let gamepadControl = () => {
    let hadleStart = null,
        controllers = {}, // 手柄控制信息,是个对象
        handleStatus = [],
        // 准备更新动画时调用此方法
        rAF = window.mozRequestAnimationFrame || window.requestAnimationFrame,
        // 取消先前通过 requestAnimationFrame 添加到计划中的动画帧请求
        rAFStop = window.mozCancelAnimationFrame || window.cancelAnimationFrame,
        notStandardMap = {};

    const getGamepadsCompatible = () => navigator.getGamepads ? navigator.getGamepads() : navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : [];

    gamepadconnected = () => {
        WelinkGamePadLoop();
    };

    gamepaddisconnected = (e) => {
        const noGamepads = getGamepadsCompatible().every(o => o === null);
        if (noGamepads) {
            rAFStop(hadleStart);
        }
        delete notStandardMap[e.gamepad.index];
    }

    // 监听手柄连接
    // 实测发现,只在第一次连接/第一次按下手柄按键时有用
    window.addEventListener('gamepadconnected', gamepadconnected);

    // 监听手柄断开
    window.addEventListener('gamepaddisconnected', gamepaddisconnected);

    // 扫描全部手柄信息
    let WelinkScanGamepads = () => {
        let gamepads = getGamepadsCompatible();
        if (!gamepads) return;
        for (let i = 0; i < gamepads.length; i++) {
            if (gamepads[i]) {
                const index = gamepads[i].index;
                if (gamepads[i].mapping !== 'standard') {
                    if (!notStandardMap[index]) {
                        notStandardMap[index] = 1;
                        console.warn(`手柄 '${index}' 按键映射是不标准的,无法使用`);
                    }
                    break;
                } else {
                    delete notStandardMap[index];
                }
                controllers[index] = gamepads[i];
            }
        }
    };

    // 物理手柄状态
    let WelinkGamePadLoop = () => {
        // 开启扫描
        WelinkScanGamepads();
        // 解析手柄
        WelinkhandleShank(controllers);
        hadleStart = null;
        hadleStart = rAF(WelinkGamePadLoop);
    };

    // 解析实体手柄
    let WelinkhandleShank = (data) => {
        for (let j in data) {
            let thisStatus = { buttons: [], axes: [] },
                handle = data[j],
                handModel = handle.id.toLowerCase();
            // 遍历按键
            for (let i = 0; i < handle.buttons.length; i++) {
                let btn = handle.buttons[i],
                    status = btn.touched ? 1 : 0,
                    conf = [4096, 8192, 16384, 32768, 256, 512, 255, 255, 32, 16, 64, 128, 1, 2, 4, 8, 0],
                    type = '';

                if (handModel.indexOf('wireless controller') != -1) {
                    // ps4应该是18个
                    conf = [4096, 8192, 16384, 32768, 256, 512, 255, 255, 32, 16, 64, 128, 1, 2, 4, 8, 0, 0];
                }

                type = i == 6 ? 'OP_XINPUT_LEFT_TRIGGER' : i == 7 ? 'OP_XINPUT_RIGHT_TRIGGER' : 'OP_XINPUT_BUTTONS';

                if (handleStatus && handleStatus[j]) {
                    // 有记录的处理
                    if (handleStatus[j].buttons[i] != status) {
                        if (btn.touched) {
                            WelinkSendHald(conf[i], type, j, conf[i]);
                        } else {
                            WelinkSendHald(0, type, j, conf[i]);
                        }
                    }
                } else {
                    // 无记录的
                    if (btn.touched) {
                        WelinkSendHald(conf[i], type, j, conf[i]);
                    }
                }
                thisStatus.buttons[i] = status;
            }
            // 处理摇杆
            let rocker = handle.axes,
                LX = Math.round(rocker[0] * 32767),
                LY = Math.round(rocker[1] * -32767),
                RX = Math.round(rocker[2] * 32767),
                RY = 0,
                CR = 0;
            // 判断是否Xbox360模式
            if (handModel.indexOf('360') != -1 || handModel.indexOf('xbox') != -1 || handModel.indexOf('wireless controller') != -1) {
                // Xbox360模式键位
                RY = Math.round(rocker[3] * -32767);
                CR = 0;
            } else {
                //安卓模式键位
                RY = Math.round(rocker[5] * -32767);
                // 处理十字键
                CR = Math.round(rocker[9] * 1000);
            }
            if (handleStatus && handleStatus[j]) {
                // 有记录的处理
                if (handleStatus[j].axes[0] != LX) {
                    WelinkSendHald(LX, 'OP_XINPUT_THUMB_LX', j);
                }
                if (handleStatus[j].axes[1] != LY) {
                    WelinkSendHald(LY, 'OP_XINPUT_THUMB_LY', j);
                }
                if (handleStatus[j].axes[2] != RX) {
                    WelinkSendHald(RX, 'OP_XINPUT_THUMB_RX', j);
                }
                if (handleStatus[j].axes[3] != RY) {
                    WelinkSendHald(RY, 'OP_XINPUT_THUMB_RY', j);
                }
                if ((handModel.indexOf('360') == -1 && handModel.indexOf('xbox') == -1) || handModel.indexOf('wireless controller') != -1) {
                    // 处理安卓模式十字键
                    if (handleStatus[j].axes[4] != CR) {
                        // 判断是否按下
                        if (CR <= 1000) {
                            if (CR == 714) {
                                WelinkSendHald(4, 'OP_XINPUT_BUTTONS', j, 4);
                            } else if (CR == 143) {
                                WelinkSendHald(2, 'OP_XINPUT_BUTTONS', j, 2);
                            } else if (CR == -429) {
                                WelinkSendHald(8, 'OP_XINPUT_BUTTONS', j, 8);
                            } else if (CR == -1000) {
                                WelinkSendHald(1, 'OP_XINPUT_BUTTONS', j, 1);
                            }
                        } else {
                            WelinkSendHald(0, 'OP_XINPUT_BUTTONS', j);
                        }
                    }
                }
            } else {
                // 无记录的
                if (LX != 0) {
                    WelinkSendHald(LX, 'OP_XINPUT_THUMB_LX', j);
                }
                if (LY != 0) {
                    WelinkSendHald(LY, 'OP_XINPUT_THUMB_LY', j);
                }
                if (RX != 0) {
                    WelinkSendHald(RX, 'OP_XINPUT_THUMB_RX', j);
                }
                if (RY != 0) {
                    WelinkSendHald(RY, 'OP_XINPUT_THUMB_RY', j);
                }
                // 判断是否安卓模式并且按下
                if ((handModel.indexOf('360') == -1 && handModel.indexOf('xbox') == -1) || handModel.indexOf('wireless controller') != -1) {
                    if (CR <= 1000) {
                        if (CR == 714) {
                            WelinkSendHald(4, 'OP_XINPUT_BUTTONS', j, 4);
                        } else if (CR == 143) {
                            WelinkSendHald(2, 'OP_XINPUT_BUTTONS', j, 2);
                        } else if (CR == -429) {
                            WelinkSendHald(8, 'OP_XINPUT_BUTTONS', j, 8);
                        } else if (CR == -1000) {
                            WelinkSendHald(1, 'OP_XINPUT_BUTTONS', j, 1);
                        }
                    } else {
                        WelinkSendHald(0, 'OP_XINPUT_BUTTONS', j);
                    }
                }
            }
            thisStatus.axes = [LX, LY, RX, RY, CR];
            // 记录状态
            handleStatus[j] = thisStatus;
        }
    };
};

// 处理虚拟手柄按键<键盘按键/touch事件>
let WelinkOnGamePadButton = (userIndex, keycode, action) => {
    if (action == KEY_DOWN) {
        WelinkSendHald(keycode * 1, 'OP_XINPUT_BUTTONS', userIndex, keycode * 1);
    } else {
        WelinkSendHald(0, 'OP_XINPUT_BUTTONS', userIndex, keycode * 1);
    }
};

// 处理虚拟手柄摇杆<键盘按键/touch事件>
let WelinkOnGamePadAxis = (userIndex, type, xValue, yValue) => {
    // 处理左摇杆
    if (type == AXIS_LXLY) {
        WelinkSendHald(xValue, 'OP_XINPUT_THUMB_LX', userIndex);
        WelinkSendHald(yValue, 'OP_XINPUT_THUMB_LY', userIndex);
    }
    // 处理右摇杆
    if (type == AXIS_RXRY) {
        WelinkSendHald(xValue, 'OP_XINPUT_THUMB_RX', userIndex);
        WelinkSendHald(yValue, 'OP_XINPUT_THUMB_RY', userIndex);
    }
    // 处理LT
    if (type == AXIS_LT) {
        WelinkSendHald(xValue, 'OP_XINPUT_LEFT_TRIGGER', userIndex);
    }
    // 处理RT
    if (type == AXIS_RT) {
        WelinkSendHald(yValue, 'OP_XINPUT_RIGHT_TRIGGER', userIndex);
    }
};

const clearAllEvents = () => {
    window.removeEventListener('gamepadconnected', gamepadconnected);
    window.removeEventListener('gamepaddisconnected', gamepaddisconnected);

    gamepadconnected = null;
    gamepaddisconnected = null;
}

///统一响应按键
let WelinkSendHald = (msg, type = 'OP_XINPUT_BUTTONS', index, action) => {
    // let index = index ? parseInt(index) : 0;
    if (type == 'OP_XINPUT_BUTTONS') {
        if (msg !== 0) {
            endMsg += action;
        } else {
            endMsg -= action;
        }
        //处理按键
    } else {
        //处理摇杆和轴等信息
    }
};