前言
本文基于如下博客基础:
大家都知道,我们一般的游戏手柄是不提供网络连接功能的,只有蓝牙、2.4G无线连接等连接方式,而这就限制了手柄的有效连接距离。
例如我的八爪鱼手柄,连接距离最远才10m,这对于远程控制来说是远远不够的,所以,我就想为我的手柄赋予网络的远距离控制能力(虽然是间接赋予 😃 )
有了如上博客的基础,我再简单介绍一下:
- 首先我们在Windows 主机上安装了python(上述pygame 安装教程有讲解);
- 并在Windows 上通过pygame 获取到了手柄的数据(程序和树莓派的一样,见上述第二篇博客);
- 然后实现了TCP 网络连接的服务器端和客户端程序的构建;
- 最后也实现了UDP 网络连接的服务器端和客户端程序的构建。
开始
如此一来,整合一下,手柄远程控制的代码就出来了:
(关键点均有注释)
TCP 版
1. 服务器端
我把树莓派作为服务器,用来接收数据,其IP地址是192.168.0.200
,端口我开的是9999
#coding:utf-8
import socket
import threading
import time
# 建立Socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('192.168.0.200', 9999))
# 开始监听端口
s.listen(5)
print('Waiting for connection...')
def tcplink(sock, addr):
print('Accept new connection from %s:%s...' % addr)
# 发送数据
sock.send(b'Welcome!')
# 接收数据
while True:
# 指定最大接收量
data = sock.recv(1024)
print data
if not data or data.decode('utf-8') == 'exit':
break
# 关闭Socket
sock.close()
print('Connection from %s:%s closed.' % addr)
# 等待连接
while True:
sock, addr = s.accept()
# 建立新进程
t = threading.Thread(target=tcplink, args=(sock, addr))
t.start()
2. 客户端
我的Windows 笔记本是客户端,读取手柄数据并发送给服务器端(树莓派)
#coding:utf-8
import pygame
import socket
# 模块初始化
pygame.init()
pygame.joystick.init()
# 若只连接了一个手柄,此处带入的参数一般都是0
joystick = pygame.joystick.Joystick(0)
# 手柄对象初始化
joystick.init()
buttons = joystick.get_numbuttons()
axes = joystick.get_numaxes()
hats = joystick.get_numhats()
done = False
# 建立连接
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('192.168.0.200', 9999))
print(s.recv(1024).decode('utf-8'))
while not done:
for event_ in pygame.event.get():
# 退出事件
if event_.type == pygame.QUIT:
done = True
# 按键按下或弹起事件
elif event_.type == pygame.JOYBUTTONDOWN or event_.type == pygame.JOYBUTTONUP:
# 获取所有按键状态信息
for i in range(buttons):
button = joystick.get_button(i)
if button:
s.send(bytes(("Button " + str(i) +" was pressed"), encoding="utf-8"))
# 轴转动事件
elif event_.type == pygame.JOYAXISMOTION:
# 获取所有轴状态信息
for i in range(axes):
axis = joystick.get_axis(i)
s.send(bytes(("axis " + str(i) + " " + str(axis) + "\n\r"), encoding="utf-8"))
# 方向键改变事件
elif event_.type == pygame.JOYHATMOTION:
# 获取所有方向键状态信息
for i in range(hats):
hat = joystick.get_hat(i)
if hat[0] == -1:
s.send(bytes('Left', encoding="utf-8"))
if hat[0] == 1:
s.send(bytes('Right', encoding="utf-8"))
if hat[1] == -1:
s.send(bytes('Down', encoding="utf-8"))
if hat[1] == 1:
s.send(bytes('Up', encoding="utf-8"))
pygame.quit()
s.send(b'exit')
# 关闭连接
s.close()
3. 结果
上面是笔记本,下面是树莓派
UDP 版
1. 服务器端
#coding=utf-8
import socket
import time
import threading
# 创建连接
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定端口
s.bind(('192.168.0.200', 9999))
print('Waiting for connection...')
while True:
# 无数据接收则线程挂起,等待数据
data, addr = s.recvfrom(1024)
print('Received from %s:%s.' % addr)
print(data)
2. 客户端
我的Windows 笔记本是客户端,读取手柄数据并发送给服务器端(树莓派)
#coding:utf-8
import pygame
import socket
# 模块初始化
pygame.init()
pygame.joystick.init()
# 若只连接了一个手柄,此处带入的参数一般都是0
joystick = pygame.joystick.Joystick(0)
# 手柄对象初始化
joystick.init()
buttons = joystick.get_numbuttons()
axes = joystick.get_numaxes()
hats = joystick.get_numhats()
done = False
# 创建连接
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while not done:
for event_ in pygame.event.get():
# 退出事件
if event_.type == pygame.QUIT:
done = True
# 按键按下或弹起事件
elif event_.type == pygame.JOYBUTTONDOWN or event_.type == pygame.JOYBUTTONUP:
# 获取所有按键状态信息
for i in range(buttons):
button = joystick.get_button(i)
if button:
s.sendto(bytes(("Button " + str(i) +" was pressed"), encoding="utf-8"), ('192.168.0.200', 9999))
# 轴转动事件
elif event_.type == pygame.JOYAXISMOTION:
# 获取所有轴状态信息
for i in range(axes):
axis = joystick.get_axis(i)
s.sendto(bytes(("axis " + str(i) + " " + str(axis) + "\n\r"), encoding="utf-8"), ('192.168.0.200', 9999))
# 方向键改变事件
elif event_.type == pygame.JOYHATMOTION:
# 获取所有方向键状态信息
for i in range(hats):
hat = joystick.get_hat(i)
if hat[0] == -1:
s.sendto(bytes('Left', encoding="utf-8"), ('192.168.0.200', 9999))
if hat[0] == 1:
s.sendto(bytes('Right', encoding="utf-8"), ('192.168.0.200', 9999))
if hat[1] == -1:
s.sendto(bytes('Down', encoding="utf-8"), ('192.168.0.200', 9999))
if hat[1] == 1:
s.sendto(bytes('Up', encoding="utf-8"), ('192.168.0.200', 9999))
pygame.quit()
s.sendto(b'exit', ('192.168.0.200', 9999))
s.close()
3. 结果
TCP 和UDP
当我们只是按下手柄的Button 时,TCP 连接能够满足快速传输的要求,但是当快速摇动控制杆时,TCP 有时会出现传输滞后的情况,但UDP 的传输质量能够一直保持相对较高的水平。
所以,对于手柄控制,我们完全可以使用UDP 进行传输,就算偶尔出现丢包也不影响整体控制。