zoukankan      html  css  js  c++  java
  • android无线游戏手柄:重力感应控制极品飞车(C#作为服务端)

    源代码:点此下载

    这篇博客是我上一篇博客的延续,之所以还要写这篇博客,是希望给大家一些灵感,写一些有趣的东西出来。

    上篇博客: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#监听端口

    View Code
    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#服务端:

    View Code
    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();
    }
    }

    重力感应:

    View Code
    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&&currentTime-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吧,哈哈。

      如果觉得不错的话,大家就顶一下本文吧,写个博客不容易呀。

      

  • 相关阅读:
    js中常见的异步操作有哪些?
    z-index
    transition和animation的区别?
    JS 中遇到有特殊字符或者空格时会被转译该怎么办?
    url参数中有+、空格、=、%、&、#等特殊符号无法显示怎么办?
    JS如何删除对象中的某一属性?
    splice和slice、map和forEach、 filter()、reduce()的区别
    css呼吸灯效果
    mescroll—移动端精致的下拉刷新和上拉加载js框架(支持Vue)
    血淋淋的教训—将Vue项目打包成app的跨域问题
  • 原文地址:https://www.cnblogs.com/fmnisme/p/2371201.html
Copyright © 2011-2022 走看看