【前端】(仅思路)如何在前端实现一个fc手柄,将手机作为游戏手柄设备。

背景

突发奇想,想要在前端实现一个fc游戏手柄,然后控制电脑的nes模拟器玩玩魂斗罗。
思路很简单,前后端使用websocket通信,connected标识socket链接已建立, 为了操作的低延时采用ws通信。

  • 前端: 实现10个按钮:上下左右,选择,开始,AB短按,AB长按。
  • 后端:监听按钮事件,然后调用win32api模拟键盘输入。
  • nes模拟器配置键盘映射。比如上=>w, 下 =>s。
    在这里插入图片描述

界面demo

在这里插入图片描述

原型图(没错,就是它,童年回忆)

在这里插入图片描述

单个按钮事件非常简单,监听touchstart 和touchend就行

        // 获取按钮元素
const leftButton = document.getElementById('left');
const topButton = document.getElementById('top');
const downButton = document.getElementById('down');
const rightButton = document.getElementById('right');

// 添加触摸按下事件监听器
leftButton.addEventListener('touchstart', function() {
    console.log('Left button touched!');
    // 在这里添加按下时的逻辑
    socket.send("a:down"); //
});

topButton.addEventListener('touchstart', function() {
    console.log('Top button touched!');
    // 在这里添加按下时的逻辑
    socket.send("w:down"); //
});

downButton.addEventListener('touchstart', function() {
    console.log('Down button touched!');
    // 在这里添加按下时的逻辑
    socket.send("s:down"); //
});

rightButton.addEventListener('touchstart', function() {
    console.log('Right button touched!');
    // 在这里添加按下时的逻辑
    socket.send("d:down"); //
});

// 添加触摸抬起事件监听器
leftButton.addEventListener('touchend', function() {
    console.log('Left button released!');
    // 在这里添加抬起时的逻辑
    socket.send("a:up"); //
});

topButton.addEventListener('touchend', function() {
    console.log('Top button released!');
    // 在这里添加抬起时的逻辑
    socket.send("w:up"); //
});

downButton.addEventListener('touchend', function() {
    console.log('Down button released!');
    // 在这里添加抬起时的逻辑
    socket.send("s:up"); //
});

rightButton.addEventListener('touchend', function() {
    console.log('Right button released!');
    // 在这里添加抬起时的逻辑
    socket.send("d:up"); //
});

但是组合键位就有问题了, html5触发 右+下 操作时,需要用两个手指点击 right 和 down才行。
PS:玩个魂斗罗还得3指操作, 这不2b的很么。
理想操作是:大拇指按下right 和down的按钮区域,就能触发右+下,尝试让gpt帮我们解决。

遇到的问题

  1. 组合键的问题:right+down, 实体手柄可以实现向右下角瞄准。但是h5中,大拇指按下right和down两个按钮区域时,只会触发一个touch事件。(这玩意儿情况一般人还真不会碰到)

实现大拇指同时按下right和down按钮区域,触发右+下的 解决办法

    // 处理触摸事件, 给方向键4个按钮 touchstart绑定此函数
    function handleTouch(event) {
        event.preventDefault(); // 阻止默认行为,如页面滚动
        const touches = event.touches;
        
        // 获取所有触摸点的位置信息
        const touchPositions = Array.from(touches).map(touch => {
            return {
                x: touch.clientX,
                y: touch.clientY
            };
        });

        // 检查是否同时触摸了 right 和 downsdsdd 按钮
        const touchingRight = touchPositions.some(pos => {
            return isTouchingElement(pos, rightButton);
        });
        const touchingDown = touchPositions.some(pos => {
            return isTouchingElement(pos, downButton);
        });
        // dssdsdsddsdsdsdsdddsdssdsdsdsdsddddssssssssddssssssdssdssd
        if (touchingRight && touchingDown) {
            // 在这里执行同时按下 right 和 down 按钮时的逻辑
            socket.send("s+d:down")
        }else if (touchingRight) {
            socket.send("d:down")
        }else if(touchingDown) {
            socket.send('s:down')
        }
    }

    // 辅助函数:检查触摸点是否在指定元素上
    function isTouchingElement(touchPosition, element) {
        const rect = element.getBoundingClientRect();
        return (
            touchPosition.x >= rect.left &&
            touchPosition.x <= rect.right &&
            touchPosition.y >= rect.top &&
            touchPosition.y <= rect.bottom
        );
    }

最终

可以愉快的拿手机当手柄了, 甚至可以两个页面双人对战。再进一步甚至能远程双人联机。

后端demo(甚至比前端逻辑更简单)

package main

import (
	"fmt"
	"log"
	"net/http"
	"text/template"

	"github.com/go-vgo/robotgo"
	"github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
}

func main() {
	fmt.Println("Starting server on port 18080...")
	http.HandleFunc("/", handler)
	http.HandleFunc("/ws", wsHandler)
	log.Fatal(http.ListenAndServe(":18080", nil))
}

// 页面返回
func handler(w http.ResponseWriter, r *http.Request) {
	tmpl, err := template.ParseFiles("index.html")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	err = tmpl.Execute(w, nil)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}
// wsHandler写的太2b了, 就不放出来了,让gpt给写写吧
func wsHandler(w http.ResponseWriter, r *http.Request) {

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值