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 )

更多相关文章

  1. android全平台基于ffmpeg解码本地MP4视频推流到RTMP服务器
  2. Android推送通知指南
  3. Android我还可以相信你多少系列文章四之悬浮窗
  4. Android我还可以相信你多少系列文章五之存储卡
  5. Android开发人员必须掌握的10 个开发工具+应该深入学习的10个开
  6. 使用Eclipse搭建简易Android服务器
  7. Android开发应用记录
  8. Android系统服务接口
  9. Android(安卓)HttpURLConnection应用技巧分享

随机推荐

  1. android用代码实现圆角背景
  2. Android添加横线和竖线分割界面
  3. Android(安卓)Hello World on IDEA
  4. Android(安卓)升级Android(安卓)X 所需要
  5. Android短信发送和监控
  6. Android之EditText练习
  7. 2019博客汇总
  8. Android(安卓)Activity getCurrentFocus(
  9. Using cygwin with the Android(安卓)NDK
  10. android 用代码画圆