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>
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值