文章目录
- 背景
- 界面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);});// dssdsdsddsdsdsdsdddsdssdsdsdsdsddddssssssssddssssssdssdssdif (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 mainimport ("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) {}