Android 手机模拟游戏手柄(USB,C#,winio)
Android 手机模拟游戏手柄(USB,C#,winio)
使用的知识点:Android服务器通过USB连接PC端,winio发送键盘消息,Socket编程,线程,Android多点触控
先说下思路,首先在Android端开启服务器程序,然后在PC端开启一个服务器程序模拟发送键盘信息(C#编写)。手机和PC用USB连接,Android和PC的通信通过Socket完成。
PC客户端程序:
虽然有很多方法可以模拟发送键盘信息如:PostMessage,keybd_event等。这些都是将按键信息发送给系统的消息队列,然后再响应。但是很多游戏使用了DirectX技术绕过了系统的消息队列。
我用了一个开源的项目,winio。可以将键盘的信息直接发给主板,这样一些游戏也可以接收了按键消息了。Winio的相关资料可以在网上搜到。由于我的系统是64位的,在使用过程中遇到了一些问题,主要是winio驱动签名的问题。具体解决方法:http://www.cnblogs.com/wangqian0realmagic/archive/2012/03/26/2418671.html
我用VS2010进行客户端的开发,这时动态载入winio64.dll时,会出现如下错误“System.DllNotFoundException……无法加载 DLL“WinIo64.dll”: 找不到指定的模块。 (异常来自 HRESULT:0x8007007E)“。是因为VS2010内部平台默认是X86的,所以要改一下,生成->配置管理器->平台,设为X64即可。
PC端和Android端的USB通信要经过端口转换,要在C#中动态使用adb.exe的forward命令。
代码:
MsgTcpClient:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
namespace GameHandles
{
class MsgTcpClient
{
//数据定义
Socket msgClient;
static int serverport = 60001;
string ip;
public MsgTcpClient()
{
msgClient = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
}
//尝试连接如果成功返回true,失败返回false
public bool Connect( string ipstring)
{
ip = ipstring;
IPEndPoint ipendpoint = new IPEndPoint(IPAddress.Parse(ip), serverport);
try
{
msgClient.Connect(ipendpoint);
return true ;
} catch
{
return false ;
}
}
//接收获得的命令
public string getMsg()
{
string msgGot = "" ;
byte [] tmpmsg = new byte [8];
int length = 0;
try
{
Console.WriteLine( "start to recieve" );
length = msgClient.Receive(tmpmsg, tmpmsg.Length, 0);
msgGot = Encoding.ASCII.GetString(tmpmsg, 0, length);
}
catch
{ }
return msgGot;
}
public void Close()
{
msgClient.Close();
}
}
}
|
主程序部分:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Diagnostics;
using System.Threading;
namespace GameHandles
{
public partial class Form1 : Form
{
//数据定义
winioManpulate winioKey;
MsgTcpClient msgClient; //接收Android服务器发来的信息
string serverip = "127.0.0.1" ;
Thread winioThread;
Keys[] keycode = {Keys.A,Keys.W,Keys.D,Keys.S,Keys.U,Keys.J,Keys.K,Keys.I};
public Form1()
{
InitializeComponent();
}
private void Form1_Load( object sender, EventArgs e)
{
winioKey = new winioManpulate();
msgClient = new MsgTcpClient();
}
//控制winio
private void changeKeys()
{
while ( true )
{
string msgGot = "" ;
msgGot = msgClient.getMsg();
Console.WriteLine(msgGot);
//Thread.Sleep(3000);
if (msgGot.Equals( "" ) == false )
{
for ( int i = 0; i < msgGot.Length; ++i)
{
if (msgGot[i] == '1' )
{
winioKey.KeyDown(keycode[i]);
//label1.Text = "keydown";
}
else
{
winioKey.KeyUp(keycode[i]);
}
}
}
}
}
//开始,设置adb,进行Tcp连接
private void btnConnect_Click( object sender, EventArgs e)
{
//设置adb
Process adbprocess = new Process();
adbprocess.StartInfo.FileName = @"adb.exe" ;
adbprocess.StartInfo.Arguments = @"forward tcp:60001 tcp:60001" ;
adbprocess.Start();
Thread.Sleep(100);
//连接server
if (msgClient.Connect(serverip) == true ) //如果连接成功
{
winioKey.Initialize();
Thread.Sleep(100);
btnStop.Enabled = true ;
btnConnect.Enabled = false ;
winioThread = new Thread( new ThreadStart(changeKeys));
winioThread.Start();
label1.Text = "begin" ;
}
}
private void btnStop_Click( object sender, EventArgs e)
{
winioThread.Abort();
msgClient.Close();
winioKey.Shutdown();
}
}
}
|
Android端:
Android作为socket服务器与普通的java程序差别不大,用ServerSocket类。只是要在AndroidManifest.xml中添加<uses-permission android:name="android.permission.INTERNET" />
还要将屏幕强制设为横屏:setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
但是在实际编码过程中遇到一些问题,我在onCreate中初始了ServerSocket对象,而setRequestedOrientation会重新执行onCreate(),这样就重复初始化了ServerSocket对象,会提示地址已占用的错误。所以先将ServerSocket对象设为null,初始化之前先判断是否为null,再初始化。
手柄的按键用多点触控的技术实现。我写了一个继承View的类HandlePanel,在onDraw方法中绘制了8个按键。然后在Activity类中设置HandlePanel.setOnTouchListener,进行相关操作。这里又遇到一个问题,我第一次用的是Android2.1的系统,这版系统中不能精确的获得是那个点出发了相应的事件,如:我在屏幕上按了两只手指,抬起一只时,无法辨别是哪只手指抬起,只能同时获得两点的坐标。在网上也没发现解决的办法后来看Android的文档,发现有个event.getActionIndex()的方法可以满足需求,但是只在2.2以上的版本有,无奈啊。
与其他Socket编程一样,ServerSocket对象要close掉,所以我重写了Activity的onStop()方法。但是每次关闭时,都提示意外退出,所以要调用super.onStop();将原先的操作也执行一遍。小错误啊,,不过也要注意一下的。
代码:
Socket服务器类:
package com.mhandle;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class MyServerSocket {
ServerSocket MyServer;
Socket mySocket;
OutputStream os;
Thread getMsg;
public MyServerSocket() {
// TODO Auto-generated constructor stub
//MyServer = new ServerSocket(60001);
mySocket = null ;
MyServer = null ;
}
public void connect() throws Exception
{
if (MyServer == null )
MyServer = new ServerSocket( 60001 );
}
public void sendMsg(String msg) throws IOException
{
if (MyServer != null )
{
if (mySocket == null )
mySocket = MyServer.accept();
os = mySocket.getOutputStream();
PrintWriter pWriter = new PrintWriter(os);
pWriter.write(msg);
pWriter.flush();
}
else
{
System.out.println( "Out Error" );
}
}
public void stop() throws IOException
{
if (mySocket != null )
mySocket.close();
if (MyServer != null )
MyServer.close();
}
}
|
HandlePanel类:
package com.mhandle;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.IBinder;
import android.util.AttributeSet;
import android.view.Display;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.View;
import android.widget.RelativeLayout;
public class HandlePanel extends View{
public HandlePanel(Context context, AttributeSet attrs, int defStyle) {
super (context, attrs, defStyle);
// TODO Auto-generated constructor stub
//init();
}
public HandlePanel(Context context, AttributeSet attrs) {
super (context, attrs);
// TODO Auto-generated constructor stub
//init();
}
public HandlePanel(Context context) {
super (context);
// TODO Auto-generated constructor stub
//init();
}
Rect hRects[];
int rectwidth = 120 ;
int VHeight;
int VWidth;
public void init( int h, int w)
{
VHeight = h;
VWidth = w;
System.out.println(VHeight+ " " +VWidth);
hRects = new Rect[ 8 ];
//方向左上右下,功能键的顺序也是如此
int tops[] = {(VHeight-rectwidth)/ 2 ,
(VHeight-rectwidth)/ 2 -(rectwidth+ 10 ),
(VHeight-rectwidth)/ 2 ,
(VHeight-rectwidth)/ 2 +(rectwidth+ 10 ),
(VHeight-rectwidth)/ 2 ,
(VHeight-rectwidth)/ 2 -(rectwidth+ 10 ),
(VHeight-rectwidth)/ 2 ,
(VHeight-rectwidth)/ 2 +(rectwidth+ 10 )
};
int lefts[] = { (VWidth/ 2 - 3 *rectwidth- 20 )/ 2 ,
(VWidth/ 2 - 3 *rectwidth- 20 )/ 2 +rectwidth+ 10 ,
(VWidth/ 2 - 3 *rectwidth- 20 )/ 2 + 2 *rectwidth+ 20 ,
(VWidth/ 2 - 3 *rectwidth- 20 )/ 2 +rectwidth+ 10 ,
VWidth/ 2 + (VWidth/ 2 - 3 *rectwidth- 20 )/ 2 ,
VWidth/ 2 + (VWidth/ 2 - 3 *rectwidth- 20 )/ 2 +rectwidth+ 10 ,
VWidth/ 2 + (VWidth/ 2 - 3 *rectwidth- 20 )/ 2 + 2 *rectwidth+ 20 ,
VWidth/ 2 + (VWidth/ 2 - 3 *rectwidth- 20 )/ 2 + rectwidth+ 10 ,
};
//System.out.println("left:" + (VWidth/2-3*rectwidth-20)/2);
//System.out.println(h+" "+w);
//for(int i=0;i<8;++i)
// lefts[i] += 60;
for ( int i= 0 ;i< 8 ;++i)
{
hRects[i] = new Rect();
hRects[i].set(lefts[i],tops[i],lefts[i]+rectwidth, tops[i]+rectwidth);
}
}
//判断是否点击到按键
public int inRect( int x, int y)
{
for ( int i= 0 ;i< 8 ;++i)
{
if ( x<hRects[i].right && x>hRects[i].left
&& y>hRects[i].top && y<hRects[i].bottom)
return i;
}
return - 1 ;
}
@Override
public void onDraw(Canvas canvas)
{
//int width = 50;
Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor( 0xFFCBD2D8 );
//绘制按键
for ( int i= 0 ;i< 8 ;++i)
{
canvas.drawRect(hRects[i], mPaint);
}
}
}
|
教训:要记录错误的名称(ClassNotFound之类),输出catch中内容,读技术文档,将网上的demo或代码测试一下,不能全信。
<script type="text/javascript"> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
Android 手机模拟游戏手柄(USB,C#,winio)相关推荐
- 一篇读懂:Android手机如何通过USB接口与外设通信(附原理分析及方案选型)
更多技术干货,欢迎扫码关注博主微信公众号:HowieXue,共同探讨软件知识经验,关注就有海量学习资料免费领哦: 目录 0背景 1.手机USB接口通信特点 1.1 使用方便 1.2 通用性强 1.3 ...
- Android手机一直连接USB进行自动化,一直充电,可能导致电池鼓包,如何定时禁止充电和开启充电?
为了避免 Android 手机在连接 USB 进行自动化测试时充电过度导致电池鼓包的问题,可以通过以下步骤实现禁止充电若干小时后自动充电的功能. 步骤: 连接 Android 手机到电脑的 USB 端 ...
- android手机使用otg usb手柄
想通过手柄来玩模拟器,但是手柄通过otg USB接口插入android手机无反应,网上搜到一些解决方法,但都不奏效,现将我的解决过程分享给大家. lsusb已经发现usb设备 但是在/dev/inpu ...
- Android 手机模拟 Mifare 卡的设计与实现
含有内嵌式安全元件的 NFC 芯片的 Android 手机可以替代校园一卡通的 Mifare 卡,可直接被已有的射频终端读取. Android 手机替代 Mifare 卡用的是 NFC 工作模式之一卡 ...
- 利用Android手机为游戏手柄,超强的AIWI体感游戏
AIWI是全球首款可以把手持装置(如:iPhone,iPod Touch和Android)当作游戏手柄来控制电脑游戏的一套软件.将您的手持装置和电脑通过蓝牙或无线网络连接起来,就可以立即体验既真实又刺 ...
- android手机怎样开启usb调试模式,Android手机USB调试在哪?安卓手机如何打开USB调试模式?...
Android手机USB调试在哪?安卓手机如何打开USB调试模式?如果我们要将安卓手机连接到电脑上,从而传输文件.下载应用或ROOT等,都需要打开手机的USB调试模式.安卓系统的版本有很多,它们的打开 ...
- android手机怎样开启usb调试模式,智能手机usb调试在哪_usb调试模式怎么打开
标签:小米(194)HTC(27)三星(1202)手机(807) 打开各手机论坛,看到许多朋友在问usb调试在哪?usb调试模式怎么打开?"USB调试"是Android系统提供的一 ...
- 夹娃娃动画Android,手机模拟抓娃娃
手机模拟抓娃娃让你通过手机足不出户也能感受娃娃机的乐趣,萌趣的卡通形象,清新治愈的设计风格,简单上手的玩法,赶快加入进来冲击最高分吧,点击下载手机模拟抓娃娃开始你的挑战! 手机模拟抓娃娃介绍 手机模拟 ...
- android 手机模拟低内存
注:需要 root 手机 原理 修改 android 系统 prop 中的 dalvik 堆大小 -dalvik.vm.heapstartsize 堆分配的初始大小,调整这个值会影响到应用的流畅性和整 ...
最新文章
- mockito_Mockito:无法实例化@InjectMocks字段:类型是接口
- 数据库设计:数据库设计的基本步骤介绍
- Quartz定时任务的基本搭建
- 什么时候用synchronized
- 两个numpy取相同值_闲谈Numpy的切片规则
- 设置密码命名是什么linux,orapwd 工具建立密码文件遵守的命名方法
- Social Dialogue征集IT意见领袖和优秀博客的RSS地址
- SQL Server数据库是否会引发恶意?
- matlab2014调用vs2015进行混合编译生成mex文件
- 实验4-1-2 求奇数和 (15 分)
- 中南大学MATLAB变量专题/矩阵的建立1-3/4
- springboot 配置过滤器不起作用的原因
- 如何解决没有指定在Windows运行或者它包含错误
- Google引擎搜索技巧
- 【生活】Java程序员的心理抗争(一)
- mysql控制台中文显示问号_控制台的中文全都是问号
- git报错the remote end hung up unexpectedlyMiB解决方法
- Arturia V Collection 9 for mac - Arturia系列合成器合集
- vue+mysql实现前端对接数据库
- php打印10以内减法表,10以内加减法口诀表练习题口算题可打印(附下载)
热门文章
- STM32F103移植uCOS-III
- uni-app上传图片并添加水印
- Navicat提示Access violation at address 004E9844 in module ‘navicat.exe’
- 26个英文字母大小写及对应的音标
- Cisco Packet Tracer 超网(CIDR)设置实验
- 查看linux snmp状态,Linux开启snmp及查询
- chrome浏览器打开网页默认全屏的方法_测试成功
- WCF,WPF,WWF 的新读音?WinCom, WinPrez, WinFlow
- 如何创建用户和组?怎样添加用户呢?windows dos命令
- div+CSS浏览器兼容问题整理(IE6.0、IE7.0 ,ie8 , FireFox...)