文章目录
- 背景
- 界面demo
- 原型图(没错,就是它,童年回忆)
- 遇到的问题
- 最终
- 后端demo(甚至比前端逻辑更简单)
背景
突发奇想,想要在前端实现一个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帮我们解决。
遇到的问题
- 组合键的问题: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) {
}