源代碼:點此下載
這篇博客是我上一篇博客的延續,之所以還要寫這篇博客,是希望給大家一些靈感,寫一些有趣的東西出來。
上篇博客:android遙控器:控制電腦上的暴風影音播放(C#作為服務端)
首先講一下手機和電腦的互聯:
1,家里有無線網路由器的話,直接將手機介入無線網就可以了。
2,只有手機和筆記本的話,可以打開android的wifi熱點。設置-無線和網絡-綁定與便攜式熱點,打開便攜式熱點。然后用筆記本連接。這里要注意一下,筆記本自動獲取ip的話,就可以通過android上網了,想阻止筆記本聯網(省流量),可以看下筆記本自動獲取到的ip和掩碼,然后將ip改為手動設置,填入剛才自動獲取的ip和掩碼,注意千萬不要設置網關和dns,不然你就等着流量耗完淚奔吧。
實現思路:
手機和筆記本使用無線網連接,手機向筆記本上的C#服務端發送控制消息,C#服務端再模擬鍵盤消息,從而控制了極品飛車。
不過這里不在使用postmessage,而是使用keybd_event(),因為keybd_event()控制鍵盤比較方便,而且飛車游戲是全屏幕運行的,所以不需要像控制暴風影音一樣,在非頂端窗口也要能接收到消息。keybd_event()就是直接模擬鍵盤消息,而不是向某一個進程發送鍵盤消息。因此哪個程序獲得焦點,keybd_event()模擬的鍵盤消息就發送給哪個。關於keybd_event()的使用方法,參考:這里
程序使用了兩個端口,一個是12121,是手機按鈕發送的消息。還有一個是12122,是重力感應發送的消息。
實現功能:
手機左右旋轉控制賽車左右方向,同時手機屏幕強制全屏橫屏顯示,程序界面上有加速,倒退,剎車等按鈕。關於重力感應x,y,z三軸的說明,參考:這里。這里我只是監控了y軸,當把手機橫着放時,y軸的值為0,向左傾斜時,值為負數,向右傾斜時,值為正數。當然,考慮到實際情況,當賽車要保持向前移動時,你的手機也不可能是完全水平放置的,所以這里我設了一個范圍,值為-2.5到2.5之間,就可以視為水平放置。
我程序里使用的是Sensor.TYPE_ACCELEROMETER傳感器,大家可以根據自己手機的實際情況,作相應的更改。
程序相關代碼:
C#服務端控制鍵盤
[DllImport("user32.dll")]
static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, uint dwExtraInfo);
[DllImport("user32.dll")]
static extern byte MapVirtualKey(byte wCode, int wMap);
/// <summary>
///left down
/// </summary>
public void LeftDown()
{
keybd_event(0x25, MapVirtualKey(0x25, 0), 0, 0);
}
/// <summary>
/// left up
/// </summary>
public void LeftUp()
{
keybd_event(0x25, MapVirtualKey(0x25, 0), 0x2, 0);
}
上面代碼中:LeftDown()方法發送了一個按住方向左鍵的消息,這里這個消息只要發送一個,極品飛車就會一直向左拐彎,直到收到松開左鍵的消息LeftUp(),因此在用重力感應控制方向的時候要判斷上次按下的鍵盤。例如:現在收到按下左鍵的消息,首先要判斷當前的狀態,如果當前已經為按下左鍵了,就可以忽略這個消息,如果上次是按下右鍵,就要先發送松開右鍵的消息,然后再發送按下左鍵。下面的靜態類實現了這個判斷:
public static class CarState
{
private static double lastNum = 0;
private static double maxNum = 2.5; //保持水平的最小值。
private static double minNum = -2.5; //保持水平的最大值
public static string Change(double num)
{
string str = null;
if (num > maxNum) //向右
{
if (lastNum <maxNum) //如果上次是向左的,就返回向右的消息,否則不用發送,因為已經向右了。
str= "ridn"; //ridn 就是right down的縮寫,其它同此。
}
else if (num < minNum) //向左
{
if (lastNum > minNum)
str = "ledn";
}
else if (num > minNum && num < maxNum) //向前
{
if(lastNum>maxNum||lastNum<minNum)
str = "notg"; //nothing
}
lastNum = num;
return str;
}
}
C#監聽端口:

class NetControl
{
SendMsg sendMsg = new SendMsg();
TextBox textBox1;
TextBox textBox2;
public NetControl(TextBox tmpTextBox1, TextBox tmpTextBox2)
{
textBox1 = tmpTextBox1;
textBox2 = tmpTextBox2;
}
/// <summary>
/// 開始監聽
/// </summary>
public void BeginListen()
{
Thread listenOneThread = new Thread(new ThreadStart(ListenOne));
listenOneThread.Start();
Thread listenTwoThread = new Thread(new ThreadStart(ListenTwo));
listenTwoThread.Start();
}
/// <summary>
/// 監聽android按鈕發送的消息
/// </summary>
private void ListenOne()
{
Thread.CurrentThread.IsBackground = true;
TcpListener server = new TcpListener(IPAddress.Any, 12121);
server.Start();
while (true)
{
TcpClient client = server.AcceptTcpClient();
client.NoDelay = true;
Thread clientThread = new Thread(new ParameterizedThreadStart(receiveMsg));
clientThread.Start(client);
}
}
/// <summary>
/// 監聽android重力感應發送的消息
/// </summary>
private void ListenTwo()
{
Thread.CurrentThread.IsBackground = true;
TcpListener server = new TcpListener(IPAddress.Any, 12122);
server.Start();
while (true)
{
TcpClient client = server.AcceptTcpClient();
client.NoDelay = true;
Thread clientThread = new Thread(new ParameterizedThreadStart(receiveMsg));
clientThread.Start(client);
}
}
/// <summary>
/// 服務器偵聽
/// </summary>
/// <param name="result"></param>
private void receiveMsg(Object obj)
{
Thread.CurrentThread.IsBackground = true;
Control.CheckForIllegalCrossThreadCalls = false;
Thread.CurrentThread.IsBackground = true;
textBox1.Text += "\r\nconnect!";
using (TcpClient client =(TcpClient)obj)
{
using (NetworkStream stream = client.GetStream())
{
int dataLength = 0;
string str;
string msg;
do
{
byte[] buffer = new byte[32];
dataLength = stream.Read(buffer, 0, buffer.Length);
str = Encoding.ASCII.GetString(buffer, 0, dataLength);
msg = Encoding.ASCII.GetString(buffer);
sendMessage(msg);
textBox1.Text += "\r\n" + msg;
} while (dataLength!=0);
}
}
}
/// <summary>
/// 根據收到信息,使用不同的功能
/// </summary>
private void sendMessage(string msg)
{
msg=msg.Substring(0,4);
switch (msg)
{
case "riup": //riup就是right up的縮寫,意思是按下方向右鍵,其它同此。
sendMsg.RightUp();
break;
case "dndn":
sendMsg.DownDown();
break;
case "dnup":
sendMsg.DownUp();
break;
case "upup":
sendMsg.UpUp();
break;
case "updn":
sendMsg.UpDown();
break;
case "sfup":
sendMsg.ShiftUp();
break;
case "sfdn":
sendMsg.ShiftDown();
break;
case "spup":
sendMsg.SpaceUp();
break;
case "spdn":
sendMsg.SpaceDown();
break;
default://接收到數字,就是重力感應發送過來的y軸的值。
double num = -1;
double.TryParse(msg, out num);
textBox2.Text +="\r\n"+ num.ToString();
if (num != -1)
{
string ctl = CarState.Change(num);
switch (ctl)
{
case "ledn":
textBox2.Text += "left";
sendMsg.RightUp();
sendMsg.LeftDown();
break;
case "ridn":
textBox2.Text += "right";
sendMsg.LeftUp();
sendMsg.RightDown();
break;
case "notg": //notg :nothing
textBox2.Text += "notg";
sendMsg.LeftUp();
sendMsg.RightUp();
break;
}
}
break;
}
}
}
android連接C#服務端:

package com.android.baofengControl;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import android.R.bool;
import android.content.Context;
import android.widget.TextView;
public class Client {
Socket client;
PrintWriter out;
TextView txt;
Context context;
int port;
public Client(int _port)
{
port=_port;
}
public Client(TextView txt,int _port)
{
this.txt=txt;
port=_port;
}
///建立連接,並保持
public void connectServer(String ip)
{
InetAddress serverAddress = null;
try {
serverAddress = InetAddress.getByName(ip);
} catch (UnknownHostException e) {
// txt.setText(e.getLocalizedMessage()+txt.getText());
e.printStackTrace();
}
try {
client=new Socket(serverAddress,port);
out=new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())),true);
} catch (IOException e) {
// txt.setText(e.getLocalizedMessage()+txt.getText());
e.printStackTrace();
}
}
public void sendMsg(String msg) throws IOException
{
out.println(msg);
}
public void close()
{
out.close();
// txt.setText("end"+txt.getText());
}
public boolean isConnected()
{
return client.isConnected();
}
}
重力感應:

package com.android.baofengControl;
import java.io.IOException;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.widget.Button;
import android.widget.TextView;
public class MySensor {
SensorManager sensorManager;
Client client;
TextView textView;
Button btnUp;
String ip;
float lastNum; //上次num的數字
float min=0; //不改變方向的最小值,小於該值想向左轉
float max=0; //不改變方向的最大值,大於該值向右轉
long lastTime=0;
long currentTime=0;
///開始監聽加速傳感器
public void Listen(SensorManager sensorManager,String _ip,Client _client)
{
client=_client;
this.sensorManager=sensorManager;
Sensor sensor=sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorManager.registerListener(sensorEventListener, sensor, SensorManager.SENSOR_DELAY_NORMAL);
}
SensorEventListener sensorEventListener =new SensorEventListener(){
public void onAccuracyChanged(android.hardware.Sensor arg0, int arg1) {
// TODO Auto-generated method stub
}
public void onSensorChanged(SensorEvent e) {
currentTime=System.currentTimeMillis();
if(client.isConnected()==true&¤tTime-lastTime>10)
{
float num=e.values[SensorManager.DATA_Y];
try {
client.sendMsg(String.valueOf(num));
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
lastTime=currentTime;
}
}};
}
使用:
打開C#服務端,打開極品飛車(我特地跑到網吧拖了一個極品飛車5,這個比較小,200多m)。
然后打開安卓客戶端,點擊連接,ok,開始游戲吧。
后續:
大家有興趣的話,還可以寫一個拳皇之類的游戲手柄,當游戲里兩個人僵持的時候,游戲里是要狂按按鈕,誰按的快就是誰贏,這里可以改成狂晃手機,誰晃得快誰就贏(思路都了,大家可以寫一個分享出來玩玩呀,),然后找個朋友,一人一台手機,控制筆記本上的游戲,瘋狂pk吧,哈哈。
如果覺得不錯的話,大家就頂一下本文吧,寫個博客不容易呀。