Unity 创建Tobii数据服务器

前言

遇到了一个眼动仪的项目,但是我没空做,给了个会cocos creator的人做,他只能用websocket或者http拿数据,捣鼓了一天.Net,很遗憾失败了,退而求其次,用Unity读到数据,并且开了个Http的服务器。
Tips:文末有工程截图

读取Tobii数据

官网连接: https://developer.tobii.com/product-integration/stream-engine/getting-started/
在.Net中用多线程拿数据会有点小问题,以后有空再说
下面是Unity代码,Start中找到设备并连接Update持续读取数据

using System;
using System.Collections;
using System.Collections.Generic;
using Tobii.StreamEngine;
using UnityEngine;

public class Yandongyi : MonoBehaviour
{
    public static Vector2 GazePoint=Vector2.zero;

    private static void OnGazePoint(ref tobii_gaze_point_t gazePoint, IntPtr userData)
    {
        // Check that the data is valid before using it
        if (gazePoint.validity == tobii_validity_t.TOBII_VALIDITY_VALID)
        {
            //Debug.Log($"Gaze point: {gazePoint.position.x}, {gazePoint.position.y}");
            GazePoint.x = gazePoint.position.x;
            GazePoint.y = gazePoint.position.y;
        }
    }

    // Create API context创建API上下文
    IntPtr apiContext;

    // Connect to the first tracker found 连接到找到的第一个跟踪器
    IntPtr deviceContext;

    tobii_error_t result;

    // Enumerate devices to find connected eye trackers 枚举设备查找连接的眼跟踪器
    List<string> urls;
    void Start()
    {
        
        result = Interop.tobii_api_create(out apiContext, null);
        Debug.Assert(result == tobii_error_t.TOBII_ERROR_NO_ERROR);

        
        result = Interop.tobii_enumerate_local_device_urls(apiContext, out urls);
        Debug.Assert(result == tobii_error_t.TOBII_ERROR_NO_ERROR);

        if (urls.Count == 0)
        {
            Console.WriteLine("Error: No device found");
            return;
        }

        
        result = Interop.tobii_device_create(apiContext, urls[0], Interop.tobii_field_of_use_t.TOBII_FIELD_OF_USE_INTERACTIVE, out deviceContext);
        Debug.Assert(result == tobii_error_t.TOBII_ERROR_NO_ERROR);

        // Subscribe to gaze data 订阅凝视数据
        result = Interop.tobii_gaze_point_subscribe(deviceContext, OnGazePoint);
        Debug.Assert(result == tobii_error_t.TOBII_ERROR_NO_ERROR);


         This sample will collect 1000 gaze points 此样品将收集1000个凝视点
        //for (int i = 0; i < 1000; i++)
        //{
        //    // Optionally block this thread until data is available. Especially useful if running in a separate thread.可选地阻止此线程,直到数据可用。如果在单独的线程中运行,则特别有用。
        //    Interop.tobii_wait_for_callbacks(new[] { deviceContext });
        //    Debug.Assert(result == tobii_error_t.TOBII_ERROR_NO_ERROR || result == tobii_error_t.TOBII_ERROR_TIMED_OUT);

        //    // Process callbacks on this thread if data is available 如果数据可用,则此线程上的处理回调
        //    Interop.tobii_device_process_callbacks(deviceContext);
        //    Debug.Assert(result == tobii_error_t.TOBII_ERROR_NO_ERROR);
        //}
    }

    // Update is called once per frame
    void Update()
    {
        if (deviceContext!=null)
        {
            // Optionally block this thread until data is available. Especially useful if running in a separate thread.
            //可选地阻止此线程,直到数据可用。如果在单独的线程中运行,则特别有用。
            Interop.tobii_wait_for_callbacks(new[] { deviceContext });
            Debug.Assert(result == tobii_error_t.TOBII_ERROR_NO_ERROR || result == tobii_error_t.TOBII_ERROR_TIMED_OUT);

            // Process callbacks on this thread if data is available 
            //如果数据可用,则此线程上的处理回调
            Interop.tobii_device_process_callbacks(deviceContext);
            Debug.Assert(result == tobii_error_t.TOBII_ERROR_NO_ERROR);
        }
        
    }
    private void OnDestroy()
    {
         Cleanup 清理
        result = Interop.tobii_gaze_point_unsubscribe(deviceContext);
        Debug.Assert(result == tobii_error_t.TOBII_ERROR_NO_ERROR);
        result = Interop.tobii_device_destroy(deviceContext);
        Debug.Assert(result == tobii_error_t.TOBII_ERROR_NO_ERROR);
        result = Interop.tobii_api_destroy(apiContext);
        Debug.Assert(result == tobii_error_t.TOBII_ERROR_NO_ERROR);
    }
}

开启Http服务器

下面是Unity开启HTTP服务器的方法

	private void HttpReceiveFunction()
    {
        try
        {
            httpListener = new HttpListener();
            httpListener.Prefixes.Add("http://+:8866/");
            httpListener.Start();
            //异步监听客户端请求,当客户端的网络请求到来时会自动执行Result委托
            //该委托没有返回值,有一个IAsyncResult接口的参数,可通过该参数获取context对象
            httpListener.BeginGetContext(Result, null);
            Debug.Log($"服务端初始化完毕http://127.0.0.1:8866/,正在等待客户端请求,时间:{DateTime.Now.ToString()}\r\n");
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            throw;
        }
    }
	/// <summary>
    /// 当接收到请求后程序流会走到这里
    /// </summary>
    /// <param name="ar"></param>
    private void Result(IAsyncResult ar)
    {
        if (!opening)
        {
            return;
        }
        //继续异步监听
        httpListener.BeginGetContext(Result, null);
        var guid = Guid.NewGuid().ToString();
        Console.ForegroundColor = ConsoleColor.White;
        //获得context对象
        HttpListenerContext context = httpListener.EndGetContext(ar);
        HttpListenerRequest request = context.Request;
        HttpListenerResponse response = context.Response;
        Console.WriteLine($"New Request:{guid},时间:{DateTime.Now.ToString()},内容:{context.Request.Url}");

        如果是js的ajax请求,还可以设置跨域的ip地址与参数
        //context.Response.AppendHeader("Access-Control-Allow-Origin", "*");//后台跨域请求,通常设置为配置文件
        //context.Response.AppendHeader("Access-Control-Allow-Headers", "ID,PW");//后台跨域参数设置,通常设置为配置文件
        //context.Response.AppendHeader("Access-Control-Allow-Method", "post");//后台跨域请求设置,通常设置为配置文件
        context.Response.ContentType = "text/plain;charset=UTF-8";//告诉客户端返回的ContentType类型为纯文本格式,编码为UTF-8
        context.Response.AddHeader("Content-type", "text/plain");//添加响应头信息
        context.Response.ContentEncoding = Encoding.UTF8;
        string returnObj = null;//定义返回客户端的信息
        switch (request.HttpMethod)
        {
            case "POST":
                {
                    //处理客户端发送的请求并返回处理信息
                    returnObj = HandleRequest(request, response);
                }
                break;
            case "GET":
                {
                    //处理客户端发送的请求并返回处理信息
                    returnObj = HandleRequest(request, response);
                }
                break;
            default:
                {
                    returnObj = "null";
                }
                break;
        }

        var returnByteArr = Encoding.UTF8.GetBytes(returnObj);//设置客户端返回信息的编码
        response.AddHeader("Content-type", "text/html;charset=UTF-8");
        response.AddHeader("Access-Control-Allow-Origin", "*");

        try
        {
            using (var stream = response.OutputStream)
            {
                //把处理信息返回到客户端
                stream.Write(returnByteArr, 0, returnByteArr.Length);
            }

        }
        catch (Exception ex)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine($"网络蹦了:{ex.ToString()}");
        }
        //Console.WriteLine($"请求处理完成:{guid},时间:{ DateTime.Now.ToString()}\r\n");
    }

    /// <summary>
    /// 处理客户端发送的请求并返回处理信息
    /// </summary>
    /// <param name="request"></param>
    /// <param name="response"></param>
    /// <returns></returns>
    private string HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
    {
        string data = null;
        try
        {
            var byteList = new List<byte>();
            var byteArr = new byte[2048];
            int readLen = 0;
            int len = 0;
            //接收客户端传过来的数据并转成字符串类型
            do
            {
                readLen = request.InputStream.Read(byteArr, 0, byteArr.Length);
                len += readLen;
                byteList.AddRange(byteArr);
            } while (readLen != 0);
            data = Encoding.UTF8.GetString(byteList.ToArray(), 0, len);

            //获取得到数据data可以进行其他操作
            //Console.WriteLine("客户端发来的是" + data+request.UserAgent);
            Console.WriteLine(Yandongyi.GazePoint.x + "-" + Yandongyi.GazePoint.y);
            return Yandongyi.GazePoint.x + "," + Yandongyi.GazePoint.y;
        }
        catch (Exception ex)
        {
            response.StatusDescription = "404";
            response.StatusCode = 404;
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine($"在接收数据时发生错误:{ex.ToString()}");
            return null;
            //return $"在接收数据时发生错误:{ex.ToString()}";//把服务端错误信息直接返回可能会导致信息不安全,此处仅供参考
        }
        response.StatusDescription = "200";//获取或设置返回给客户端的 HTTP 状态代码的文本说明。
        response.StatusCode = 200;// 获取或设置返回给客户端的 HTTP 状态代码。
        Console.ForegroundColor = ConsoleColor.Green;
        Console.WriteLine($"接收数据完成:{data.Trim()},时间:{DateTime.Now.ToString()}");
        return $"接收数据完成";
    }

开启服务器并获取数据完整源码(需结合读取Tobii数据)

场景截图

using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Text;
using UnityEngine;

public class YandongyiServer : MonoBehaviour
{
    public GameObject MyCube;
    public Material redMat;
    public Material greenMat;


    private HttpListener httpListener;

    private bool opening = false;
    // Start is called before the first frame update
    void Start()
    {
        // 设置分辨率和是否全屏
        Screen.SetResolution(1024, 768, false);
        MyCube.GetComponent<MeshRenderer>().material = redMat;
    }
    private void Update()
    {
        if (MyCube!=null)
        {
            MyCube.transform.position = new Vector3(Yandongyi.GazePoint.x*10-5,-(Yandongyi.GazePoint.y*6-3));
        }
    }

    public void StartHttpServer()
    {
        if (!opening)
        {
            opening = true;
            HttpReceiveFunction();
            MyCube.GetComponent<MeshRenderer>().material = greenMat;
        }
    }
    public void CloseHttpServer()
    {
        if (opening)
        {
            opening = false;
            if (httpListener.IsListening)
            {
                httpListener.Stop();
                httpListener = null;
            }
            MyCube.GetComponent<MeshRenderer>().material = redMat;
        }
    }
    private void HttpReceiveFunction()
    {
        try
        {
            httpListener = new HttpListener();
            httpListener.Prefixes.Add("http://+:8866/");
            httpListener.Start();
            //异步监听客户端请求,当客户端的网络请求到来时会自动执行Result委托
            //该委托没有返回值,有一个IAsyncResult接口的参数,可通过该参数获取context对象
            httpListener.BeginGetContext(Result, null);
            Debug.Log($"服务端初始化完毕http://127.0.0.1:8866/,正在等待客户端请求,时间:{DateTime.Now.ToString()}\r\n");
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            throw;
        }
    }
    /// <summary>
    /// 当接收到请求后程序流会走到这里
    /// </summary>
    /// <param name="ar"></param>
    private void Result(IAsyncResult ar)
    {
        if (!opening)
        {
            return;
        }
        //继续异步监听
        httpListener.BeginGetContext(Result, null);
        var guid = Guid.NewGuid().ToString();
        Console.ForegroundColor = ConsoleColor.White;
        //获得context对象
        HttpListenerContext context = httpListener.EndGetContext(ar);
        HttpListenerRequest request = context.Request;
        HttpListenerResponse response = context.Response;
        Console.WriteLine($"New Request:{guid},时间:{DateTime.Now.ToString()},内容:{context.Request.Url}");

        如果是js的ajax请求,还可以设置跨域的ip地址与参数
        //context.Response.AppendHeader("Access-Control-Allow-Origin", "*");//后台跨域请求,通常设置为配置文件
        //context.Response.AppendHeader("Access-Control-Allow-Headers", "ID,PW");//后台跨域参数设置,通常设置为配置文件
        //context.Response.AppendHeader("Access-Control-Allow-Method", "post");//后台跨域请求设置,通常设置为配置文件
        context.Response.ContentType = "text/plain;charset=UTF-8";//告诉客户端返回的ContentType类型为纯文本格式,编码为UTF-8
        context.Response.AddHeader("Content-type", "text/plain");//添加响应头信息
        context.Response.ContentEncoding = Encoding.UTF8;
        string returnObj = null;//定义返回客户端的信息
        switch (request.HttpMethod)
        {
            case "POST":
                {
                    //处理客户端发送的请求并返回处理信息
                    returnObj = HandleRequest(request, response);
                }
                break;
            case "GET":
                {
                    //处理客户端发送的请求并返回处理信息
                    returnObj = HandleRequest(request, response);
                }
                break;
            default:
                {
                    returnObj = "null";
                }
                break;
        }

        var returnByteArr = Encoding.UTF8.GetBytes(returnObj);//设置客户端返回信息的编码
        response.AddHeader("Content-type", "text/html;charset=UTF-8");
        response.AddHeader("Access-Control-Allow-Origin", "*");

        try
        {
            using (var stream = response.OutputStream)
            {
                //把处理信息返回到客户端
                stream.Write(returnByteArr, 0, returnByteArr.Length);
            }

        }
        catch (Exception ex)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine($"网络蹦了:{ex.ToString()}");
        }
        //Console.WriteLine($"请求处理完成:{guid},时间:{ DateTime.Now.ToString()}\r\n");
    }

    /// <summary>
    /// 处理客户端发送的请求并返回处理信息
    /// </summary>
    /// <param name="request"></param>
    /// <param name="response"></param>
    /// <returns></returns>
    private string HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
    {
        string data = null;
        try
        {
            var byteList = new List<byte>();
            var byteArr = new byte[2048];
            int readLen = 0;
            int len = 0;
            //接收客户端传过来的数据并转成字符串类型
            do
            {
                readLen = request.InputStream.Read(byteArr, 0, byteArr.Length);
                len += readLen;
                byteList.AddRange(byteArr);
            } while (readLen != 0);
            data = Encoding.UTF8.GetString(byteList.ToArray(), 0, len);

            //获取得到数据data可以进行其他操作
            //Console.WriteLine("客户端发来的是" + data+request.UserAgent);
            Console.WriteLine(Yandongyi.GazePoint.x + "-" + Yandongyi.GazePoint.y);
            return Yandongyi.GazePoint.x + "," + Yandongyi.GazePoint.y;
        }
        catch (Exception ex)
        {
            response.StatusDescription = "404";
            response.StatusCode = 404;
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine($"在接收数据时发生错误:{ex.ToString()}");
            return null;
            //return $"在接收数据时发生错误:{ex.ToString()}";//把服务端错误信息直接返回可能会导致信息不安全,此处仅供参考
        }
        response.StatusDescription = "200";//获取或设置返回给客户端的 HTTP 状态代码的文本说明。
        response.StatusCode = 200;// 获取或设置返回给客户端的 HTTP 状态代码。
        Console.ForegroundColor = ConsoleColor.Green;
        Console.WriteLine($"接收数据完成:{data.Trim()},时间:{DateTime.Now.ToString()}");
        return $"接收数据完成";
    }
    private void OnDestroy()
    {
        CloseHttpServer();
    }
}

运行示例

修正

收到消息的时候用这个读取解析

/// <summary>
/// 处理客户端发送的请求并返回处理信息
/// </summary>
/// <param name="request"></param>
/// <param name="response"></param>
/// <returns></returns>
private string HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
{
    string data = null;
    try
    {
        var byteList = new List<byte>();
        int readLength = 1024;
        byte[] byteArr = new byte[readLength];
        int totalBytesRead = 0;

        // 接收客户端传过来的数据并转成字符串类型
        while (totalBytesRead < request.ContentLength64)
        {
            int bytesRead = request.InputStream.Read(byteArr, 0, readLength);
            if (bytesRead == 0)
            {
                break; // 如果读取到流的末尾,跳出循环
            }
            totalBytesRead += bytesRead;
            byteList.AddRange(byteArr.Take(bytesRead)); // 只添加实际读取的部分到 byteList
            Debug("Sl正在接受数据当前接受长度为--->" + totalBytesRead + ", 应该接收长度为" + request.ContentLength64);
        }

        // 将接收到的字节数据转换为字符串
        data = Encoding.UTF8.GetString(byteList.ToArray());

        // 处理请求并返回处理结果
        string returnObj = "";
        CombatResultMsg responsemsg = mainGameSceneLite.GetCombatSimulateResult(data);

        if (responsemsg.success)
        {
            returnObj = JsonConvert.SerializeObject(responsemsg);
        }
        else
        {
            response.StatusDescription = "200,发生错误";
            response.StatusCode = 200;
        }
        return returnObj;
    }
    catch (Exception ex)
    {
        response.StatusDescription = "404";
        response.StatusCode = 404;
        Error($"在接收数据时发生错误:{ex.ToString()}");
        return null;
    }
}

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值