这个是我很早之前就想写的,但是最近一直比较忙。知乎上有很多小伙伴留言或者私信问过我这个怎么做,但是一直没时间写,再加上个人比较懒,所以就拖更了。这次正好赶上五一假期,大概放三四个月吧(其实是暑假),就来写一下怎么搞。下面开始!

原理

目前这个只支持安卓设备,因为要用到ADB。主要的原理就是树莓派通过ADB向手机发送控制信号,手机端APP收到信号后就操作手机做出相应动作。比如说你按下W,对应的操作就是向上滑动,那么树莓派就会发送这个信号给手机,手机便会根据收到的数据做出滑动的动作。

方法

下载源码

git clone https://github.com/haoyeel/pubg-phone.git
cd pubg-phone

这是我同学的GitHub仓库,目前在华为南研所搬砖,单身狗一个,有想法的直接在下面留言,联系方式啥的私信要(限女生)!

树莓派端

#coding: utf-8
from websocket import create_connection

from evdev import InputDevice
from select import select

from threading import Thread
import queue
import json
import time
import sys
import os

#code of json, only @ACTION_DOWN,@ACTION_MOVE,@ACTION_UP related
#position @X,@y may need change according to your phone
#{"code": ,"action": ,"X": ,"Y": }

#may need change according to your system
keyboard_dev_position = '/dev/input/event1'
mouse_dev_position = '/dev/input/event4'
input_event_codes = {'type_keyboard_key':1, 'type_mouse_move':2, 'type_mouse_key':1}

#code, 4 contacts need most
g_motionCode = {'CODE_WASD':0, 'CODE_MOUSE':1, 'CODE_CLICK':2, 'CODE_VIEW':3, 'CODE_CLICK_2':4, 'CODE_LOCAL_EXIT':10}
#action, same int number with Android MotionEvent
g_motionEvent = {'ACTION_DOWN':0, 'ACTION_UP':1, 'ACTION_MOVE':2}

g_keyboard_mouse_status = {'wasd':0, 'mouse_key':0, 'mouse_move':0}

#mouse area limit
g_mouse_area = {'center_x':1900, 'center_y':490, 'top_y':90, 'bottom_y':890, 'left_x':1500, 'right_x':2300}

#mouse absolute position history
g_mouse_position_abs_his = {'hor':-1, 'ver':-1}
g_mouse_touchscreen_abs_his = {'x':g_mouse_area['center_x'], 'y':g_mouse_area['center_y']}

#game interface status
g_game_interface_status = {'gun_num':1, 'car':0, 'swim':0 }

def keyboardAction(tx_queue, k_event):
    keyboard_mouse_status_old = {'wasd':g_keyboard_mouse_status['wasd']}

    #first, update the @g_keyboard_mouse_status
    if (k_event.value == 1):        #key pressed
        if k_event.code == 17:      #W
            if (g_keyboard_mouse_status['wasd'] == 0 or g_keyboard_mouse_status['wasd'] == 0x02 or g_keyboard_mouse_status['wasd'] == 0x08):
                g_keyboard_mouse_status['wasd'] |= 0x01;
        elif k_event.code == 30:    #A
            #if(g_keyboard_mouse_status['wasd'] == 0 or g_keyboard_mouse_status['wasd'] == 0x01 or g_keyboard_mouse_status['wasd'] == 0x04):
            g_keyboard_mouse_status['wasd'] &= 0xF5;
            g_keyboard_mouse_status['wasd'] |= 0x02;
        elif k_event.code == 31:    #S
            if(g_keyboard_mouse_status['wasd'] == 0 or g_keyboard_mouse_status['wasd'] == 0x02 or g_keyboard_mouse_status['wasd'] == 0x08):
                g_keyboard_mouse_status['wasd'] |= 0x04;
        elif k_event.code == 32:    #D
            #if(g_keyboard_mouse_status['wasd'] == 0 or g_keyboard_mouse_status['wasd'] == 0x01 or g_keyboard_mouse_status['wasd'] == 0x04):
            g_keyboard_mouse_status['wasd'] &= 0xF5;
            g_keyboard_mouse_status['wasd'] |= 0x08;
        elif k_event.code == 57:    #space for jump
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_DOWN'], 2256, 755 ])
        elif k_event.code == 46:    #C for 
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_DOWN'], 2038, 1003 ])
        elif k_event.code == 44:    #Z
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_DOWN'], 2226, 971 ])
        elif k_event.code == 16:    #Q
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_DOWN'], 357, 393 ])
        elif k_event.code == 18:    #E
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_DOWN'], 515, 393 ])
        elif k_event.code == 2:     #1
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_DOWN'], 1085, 977 ])
        elif k_event.code == 3:     #2
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_DOWN'], 1340, 977 ])
        elif k_event.code == 4:     #3
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_DOWN'], 1574, 1012 ])
        elif k_event.code == 5:     #4
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_DOWN'], 850, 1012 ])
        elif k_event.code == 19:    #R
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_DOWN'], 1883, 1015 ])
        elif k_event.code == 33:    #F
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_DOWN'], 1714, 457 ])
        elif k_event.code == 15:    #Tab for map
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_DOWN'], 2295, 51 ])
        elif k_event.code == 41:    #` for bag
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_DOWN'], 230, 980 ])
        elif k_event.code == 45:    #X for pick up close
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_DOWN'], 1798, 276 ])
            tx_queue.put([ g_motionCode['CODE_CLICK_2'], g_motionEvent['ACTION_DOWN'], 1709, 276 ])
        elif k_event.code == 42:    #Shift
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_DOWN'], 1970, 838 ])
        elif k_event.code == 48:    #B
            if g_game_interface_status['gun_num'] == 1:
                tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_DOWN'], 1085, 909 ])
            elif g_game_interface_status['gun_num'] == 2:
                tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_DOWN'], 1340, 909 ])

        else:
            print("Undefined key pressed.");
    elif (k_event.value == 0):      #key relessed
        if k_event.code == 17:      #W
            g_keyboard_mouse_status['wasd'] &= 0xFE;
        elif k_event.code == 30:    #A
            g_keyboard_mouse_status['wasd'] &= 0xFD;
        elif k_event.code == 31:    #S
            g_keyboard_mouse_status['wasd'] &= 0xFB;
        elif k_event.code == 32:    #D
            g_keyboard_mouse_status['wasd'] &= 0xF7;
        elif k_event.code == 57:    #space
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_UP'], 2256, 755 ])
        elif k_event.code == 46:    #C for 
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_UP'], 2038, 1003 ])
        elif k_event.code == 44:    #Z
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_UP'], 2226, 971 ])
        elif k_event.code == 16:    #Q
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_UP'], 357, 393 ])
        elif k_event.code == 18:    #E
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_UP'], 515, 393 ])
        elif k_event.code == 2:     #1
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_UP'], 1085, 977 ])
            g_game_interface_status['gun_num'] = 1
        elif k_event.code == 3:     #2
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_UP'], 1340, 977 ])
            g_game_interface_status['gun_num'] = 2
        elif k_event.code == 4:     #3
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_UP'], 1574, 1012 ])
        elif k_event.code == 5:     #4
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_UP'], 850, 1012 ])
        elif k_event.code == 19:    #R
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_UP'], 1883, 1015 ])
        elif k_event.code == 33:    #F
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_UP'], 1714, 457])
        elif k_event.code == 15:    #Tab for map
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_UP'], 2295, 51 ])
        elif k_event.code == 41:    #` for bag
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_UP'], 230, 980 ])
        elif k_event.code == 45:    #X for pick up close
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_UP'], 1798, 276 ])
            tx_queue.put([ g_motionCode['CODE_CLICK_2'], g_motionEvent['ACTION_UP'], 1709, 276 ])
        elif k_event.code == 42:    #Shift
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_UP'], 1970, 838 ])
        elif k_event.code == 48:    #B
            if g_game_interface_status['gun_num'] == 1:
                tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_UP'], 1085, 909 ])
            elif g_game_interface_status['gun_num'] == 2:
                tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_UP'], 1340, 909 ])
        else:
            print("Undefined key relessed.")
    else:
        return

    #then, determine the action
    if g_keyboard_mouse_status['wasd'] != keyboard_mouse_status_old['wasd']:    #new key event occurred
        if (keyboard_mouse_status_old['wasd'] == 0 and g_keyboard_mouse_status['wasd'] != 0):   #key pressed, need @ACTION_DOWN first, then we can @ACTION_MOVE
            tx_queue.put([ g_motionCode['CODE_WASD'], g_motionEvent['ACTION_DOWN'], 450, 809 ])

        if g_keyboard_mouse_status['wasd'] == 0x01:   #W
            tx_queue.put([ g_motionCode['CODE_WASD'], g_motionEvent['ACTION_MOVE'], 450, 509 ])
        elif g_keyboard_mouse_status['wasd'] == 0x03: #WA
            tx_queue.put([ g_motionCode['CODE_WASD'], g_motionEvent['ACTION_MOVE'], 200, 509 ])
        elif g_keyboard_mouse_status['wasd'] == 0x02: #A
            tx_queue.put([ g_motionCode['CODE_WASD'], g_motionEvent['ACTION_MOVE'], 200, 809 ])
        elif g_keyboard_mouse_status['wasd'] == 0x06: #AS
            tx_queue.put([ g_motionCode['CODE_WASD'], g_motionEvent['ACTION_MOVE'], 200, 1059 ])
        elif g_keyboard_mouse_status['wasd'] == 0x04: #S
            tx_queue.put([ g_motionCode['CODE_WASD'], g_motionEvent['ACTION_MOVE'], 450, 1059 ])
        elif g_keyboard_mouse_status['wasd'] == 0x0C: #SD
            tx_queue.put([ g_motionCode['CODE_WASD'], g_motionEvent['ACTION_MOVE'], 700, 1059 ])
        elif g_keyboard_mouse_status['wasd'] == 0x08: #D
            tx_queue.put([ g_motionCode['CODE_WASD'], g_motionEvent['ACTION_MOVE'], 700, 809 ])
        elif g_keyboard_mouse_status['wasd'] == 0x09: #DW
            tx_queue.put([ g_motionCode['CODE_WASD'], g_motionEvent['ACTION_MOVE'], 700, 509 ])
        elif g_keyboard_mouse_status['wasd'] == 0x00: #WASD UP
            if keyboard_mouse_status_old['wasd'] != 0:
                tx_queue.put([ g_motionCode['CODE_WASD'], g_motionEvent['ACTION_UP'], 450, 809 ])

def mouseActionMove(tx_queue, m_event):
    keyboard_mouse_status_old = {'mouse_key':g_keyboard_mouse_status['mouse_key']}

    hor_relative = 0
    ver_relative = 0
    #if "/dev/input/event2" data format is absolute, start here
    '''if g_mouse_position_abs_his['hor'] == -1 or g_mouse_position_abs_his['ver'] == -1:
        if m_event.code == 0:
            g_mouse_position_abs_his['hor'] = int(m_event.value / 30)
        if m_event.code == 1:
            g_mouse_position_abs_his['ver'] = int(m_event.value /30)
        return

    if m_event.code == 0: #mouse move horizontal
        hor_relative = int(m_event.value / 30) - g_mouse_position_abs_his['hor']
        g_mouse_position_abs_his['hor'] += hor_relative
    elif m_event.code == 1: #mouse move vertical
        ver_relative = int(m_event.value / 30) - g_mouse_position_abs_his['ver']
        g_mouse_position_abs_his['ver'] += ver_relative'''

    #if "/dev/input/event2" data format is relative, start here
    if m_event.code == 0: #mouse move horizontal
        hor_relative = int(m_event.value)
    elif m_event.code == 1: #mouse move vertical
        ver_relative = int(m_event.value)

    mouse_touchscreen_abs_x = g_mouse_touchscreen_abs_his['x'] + hor_relative
    mouse_touchscreen_abs_y = g_mouse_touchscreen_abs_his['y'] + ver_relative

    #make sure pos in limit area
    if( mouse_touchscreen_abs_x < g_mouse_area['left_x'] or mouse_touchscreen_abs_x > g_mouse_area['right_x'] or mouse_touchscreen_abs_y < g_mouse_area['top_y'] or mouse_touchscreen_abs_y > g_mouse_area['bottom_y']):
        print('area limit')
        tx_queue.put([ g_motionCode['CODE_MOUSE'], g_motionEvent['ACTION_UP'], g_mouse_touchscreen_abs_his['x'], g_mouse_touchscreen_abs_his['y'] ])
        g_mouse_touchscreen_abs_his['x'] = g_mouse_area['center_x']
        g_mouse_touchscreen_abs_his['y'] = g_mouse_area['center_y']
        mouse_touchscreen_abs_x = g_mouse_touchscreen_abs_his['x'] + hor_relative
        mouse_touchscreen_abs_y = g_mouse_touchscreen_abs_his['y'] + ver_relative
        g_keyboard_mouse_status['mouse_move'] = 0

    if g_keyboard_mouse_status['mouse_move'] == 0:    #mouse move not begain
        g_keyboard_mouse_status['mouse_move'] = 1
        tx_queue.put([ g_motionCode['CODE_MOUSE'], g_motionEvent['ACTION_DOWN'], g_mouse_area['center_x'], g_mouse_area['center_y'] ])

    tx_queue.put([ g_motionCode['CODE_MOUSE'], g_motionEvent['ACTION_MOVE'], mouse_touchscreen_abs_x, mouse_touchscreen_abs_y ])
    g_mouse_touchscreen_abs_his['x'] = mouse_touchscreen_abs_x
    g_mouse_touchscreen_abs_his['y'] = mouse_touchscreen_abs_y

def mouseActionKey(tx_queue, m_event):
    if m_event.code == 272:
        if m_event.value == 1:
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_DOWN'], 215, 555 ])
        elif m_event.value == 0:
            tx_queue.put([ g_motionCode['CODE_CLICK'], g_motionEvent['ACTION_UP'], 555, 555 ])
    if m_event.code == 273:
        if m_event.value == 1:
            tx_queue.put([ g_motionCode['CODE_CLICK_2'], g_motionEvent['ACTION_DOWN'], 2265, 563 ])
        elif m_event.value == 0:
            tx_queue.put([ g_motionCode['CODE_CLICK_2'], g_motionEvent['ACTION_UP'], 2265, 563 ])
    elif m_event.code == 274:   #roll for solve the false issue
        tx_queue.put([ g_motionCode['CODE_MOUSE'], g_motionEvent['ACTION_UP'], g_mouse_touchscreen_abs_his['x'], g_mouse_touchscreen_abs_his['y'] ])
        g_mouse_position_abs_his['hor'] = -1
        g_mouse_position_abs_his['ver'] = -1
        g_mouse_touchscreen_abs_his['x'] = g_mouse_area['center_x']
        g_mouse_touchscreen_abs_his['y'] = g_mouse_area['center_y']
        g_keyboard_mouse_status['mouse_move'] = 0

def threadKeyboard(tx_queue):
    keyboard_dev = InputDevice(keyboard_dev_position)
    while True:
        select([keyboard_dev], [], [])
        for keyboard_event in keyboard_dev.read():
            #print("<K>type%s, code:%s, value:%s" % (keyboard_event.type, keyboard_event.code, keyboard_event.value))
            if keyboard_event.type == input_event_codes['type_keyboard_key']:
                keyboardAction(tx_queue, keyboard_event)

def threadMouse(tx_queue):
    mouse_dev = InputDevice(mouse_dev_position)
    while True:
        select([mouse_dev], [], [])
        for mouse_event in mouse_dev.read():
            #print("<M>type%s, code:%s, value:%s" % (mouse_event.type, mouse_event.code, mouse_event.value))
            if mouse_event.type == input_event_codes['type_mouse_move']:
                mouseActionMove(tx_queue, mouse_event)
            elif mouse_event.type == input_event_codes['type_mouse_key']:
                mouseActionKey(tx_queue, mouse_event)

if __name__ == "__main__":
    #res = os.system('sh ./start-server.sh &')
    #if res != 0:
    #   print("Start app failed!")
    #   sys.exit()
    #time.sleep(3)
    tx_queue = queue.Queue(maxsize = 10)
    thread_keyboard = Thread(target = threadKeyboard, args = (tx_queue, ))
    thread_mouse = Thread(target = threadMouse, args = (tx_queue, ))
    thread_keyboard.start()
    thread_mouse.start()
    ws = create_connection("ws://127.0.0.1:10000/input")
    while True:
        tx_data = tx_queue.get()
        tx_data_json = json.dumps({'code':tx_data[0], 'action':tx_data[1], 'X':tx_data[2], 'Y':tx_data[3]})
        ws.send(tx_data_json)
    print("FUCKED.")
    ws.close()

这个是树莓派端的代码,在python文件夹下,主要涉及两个方面:一是网络通信,里面我们可以看到使用了websocket,这部分很简单,就是发送数据到本机的10000号端口;二是接收键盘和鼠标的动作,这部分根据手机不同需要做一定调整,具体请看14-18行的注释。第二部分需要修改是因为不同的手机屏幕尺寸不一样,导致按键坐标不一样,这部分可以通过设置→开发者选项→指针位置确定每个按键的坐标。

其中,按键被分为按下以及滑动两种,WASD作为方向键,属于滑动组,而其余按键如偏头属于按下组。鼠标的动作同样分为按下和滑动,点击属于按下动作,指针的移动属于滑动。keyboardAction函数处理键盘的动作,mouseActionKeymouseActionMove处理鼠标的动作。在主函数中分别通过两个线程处理键盘和鼠标的动作。

上图所示的代码根据手机屏幕大小需要修改(事实上代码里的数字基本上都要改动),红框处设置的是没有按下WASD时的方向转盘中心点的位置,这个地方必改。

手机端

手机端通过接收树莓派发送的JSON数组来决定如何做出动作。这部分代码不需要作出改动,因此不做过多的描述。

如何运行

为了能够正确的运行程序,首先需要安装adb:

sudo apt-get upgrade
sudo apt-get update
sudo apt-get install adb

在开始使用前需要通过ADB先连接手机,具体方法可以百度Linux下adb的使用方法,照着操作就行。完成后先启动手机端APP,然后在树莓派上执行以下命令:

cd python
sudo bash start-server.sh

至此,整个过程已经完成了,但是距离正常使用还有一些调试工作需要完成,具体就是按键的坐标需要对应,建议打开设置→开发者选项→指针位置以捕获树莓派的动作以确定目前的实际运动轨迹,并根据屏幕上按键的位置改动树莓派端代码中的数字。

有问题可以留言讨论!

最后修改日期:2020年5月2日

作者

留言

Fred Qhin 

限女生?,女生怎么会看这种东西

可以要您的联系方式嘛

发表评论