Unity开发展厅中控系统


前言

记录自己独自完成的一个完整的展厅中控系统。整体实现思路:灯光线路接上网络继电器,网络继电器接上串口服务器,串口服务器接上路由器,电脑接上路由器,华为paid连接路由器无线网络,实现所有设备连接在一个局域网内。 中控的主要功能: 1.控制灯光的开关 2.控制电脑开关机 3.控制电视机的开机和关机 4.控制内容: a、PPT的上下翻页(有动态效果的) b、视频的暂停、播放、停止 c、系统声音的加减

一、硬件环境

交换机:光电信息转换
路由器:创建局域网络,连接不同的网络设备
AC控制器:管理所有AP设备(无线WIFI)
串口服务器:可以直连传统串口设备(不带IP的模块),通过网络协议发送指令给模块
时序电源:提送稳定可控的电源(可通过IP或串口控制各个电源开闭)
网络继电器:通过网络控制灯光开关
电脑:每台电脑作为单独的服务端
电视机:这里用的是电视机作为显示器
华为paid:安卓系统,作为客户端给每台电脑发送指令

二、整体构造

1.灯光控制模块

灯光连接智能照明模块(其实就是一个继电器),智能照明模块再与串口服务器连接

实现:串口服务器作为服务端,自己开发一个客户端,通过网络协议(我这里是UDP和端口号)连接到串口服务器上,发送指令,实现智能照明模块的开口 开闭合控制灯光的开关

注意事项:智能照明模块的波特率要与串口服务器的波特率设置一致,网络协议(在串口服 务器内也可设置)要确定好(UDP或者是TCP);
确定好智能照明模块的指令(每个模块都有自己的指令,说明书或者供应商提供)

代码如下:

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

public class SendInstructionsToLamp : MonoBehaviour
{
    public Socket socket;

    public int _port = 9600;
    public string _ip = "192.168.0.7";
    void Start()
    {
        ConnectLight();
    }


    /// <summary>
    /// 通过TCP协议连接智能照明模块(灯组控制部分)
    /// </summary>
    public void ConnectLight()
    {
        try
        {


            //创建客户端Socket,获得远程ip和端口号
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            IPAddress ip = IPAddress.Parse(_ip);
            IPEndPoint point = new IPEndPoint(ip, _port);

            socket.Connect(point);
            Debug.Log("连接成功!");
        }
        catch (Exception)
        {
            Debug.Log("IP或者端口号错误...");
        }

    }

    bool quankai;
    List<byte[]> instructions_temp;
    /// <summary>
    /// 给智能照明模块发送指令(灯组控制部分)
    /// </summary>
    /// <param name="instructions">发送指令的list集合</param>
    /// <param name="timeSpan">发送指令的时间间隔</param>
    public void SendInstructions(List<byte[]> instructions, float timeSpan) {
        StartCoroutine(SendInstructions_TimeSpan(instructions, timeSpan));
    }

    IEnumerator SendInstructions_TimeSpan(List<byte[]> instructions, float timeSpan) {
        for (int k = 0; k < 10; k++)//这里为什么要做一个循环发送指令,是因为在实际测试中发现,发送一两次指令不稳定,多发送几次确保指令能被网络继电器接收到
        {
            for (int i = 0; i < instructions.Count; i++)
            {
                byte[] b = new byte[8];
                socket.Send(instructions[i]);
                yield return new WaitForSeconds(0.1f);
            }

        }
        
        yield return new WaitForSeconds(timeSpan);
        //StopAllCoroutines();
    }

    //连接关闭
    void SocketQuit() {
        socket.Close();
    }

    private void OnApplicationQuit()
    {
        Debug.Log(123456);
        SocketQuit();
    }

    /// <summary>
    /// 一键照明指令
    /// </summary>
    /// <param name="b">true为全开、false为全关</param>
    public void One_KeyLightSwitch(bool b) {
        if (b)
        {
            List<byte[]> list_byte1 = new List<byte[]>();
            byte[] bt1 = new byte[8] { 0x04, 0x06, 0x00, 0x02, 0x00, 0x01, 0xE9, 0x9F };
            list_byte1.Add(bt1);
            SendInstructions(list_byte1, 0);
            Debug.Log("照明全开!");
        }
        else {
            List<byte[]> list_byte2 = new List<byte[]>();
            byte[] bt1 = new byte[8] { 0x04, 0x06, 0x00, 0x01, 0x00, 0x00, 0xD8, 0x5F };
            list_byte2.Add(bt1);
            SendInstructions(list_byte2, 0);
            Debug.Log("照明全关!");
        }
    }
}

2.主中控模块

代码如下:

客户端

using UnityEngine;
using System;
//using System.IO.Ports;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Text;
using UnityEngine.UI;

public class SpSend : MonoBehaviour
{
    public static SpSend _instance;

    public Toggle[] toggles;
    public Toggle toggle1;



    public Socket socketSend;
    Socket socketSend_Tcp_Udp;

    string ip;

    private void Awake()
    {
        _instance = this;
    }

    private void Start()
    {
        bt_connect_Click("192.168.0.101");
    }
    public QiehuanVideoPPT_1 qiehuanVideoPPT_1;

    public void Send_Udp_String(string s)
    {
        byte[] b = Encoding.ASCII.GetBytes(s.ToCharArray());
        socketSend_Tcp_Udp.Send(b);
    }



    public void bt_connect_Click(string _ip)
    {
        if (socketSend != null)
            socketSend = null;
        try
        {
            int _port = 8888;
            socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            IPAddress ip = IPAddress.Parse(_ip);
            IPEndPoint point = new IPEndPoint(ip, _port);

            socketSend.Connect(point);
            Debug.Log("连接成功!");
        }
        catch (Exception)
        {
            Debug.Log("IP或者端口号错误...");
        }

    }



    public void SendMassageToClient(string s) {
        //string s = "u:video1.mp4";
        Debug.Log(s);
        byte[] b = Encoding.ASCII.GetBytes(s.ToCharArray());

        socketSend.Send(b);
        //socketSend.Close();
    }

}

服务端

using UnityEngine;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine.Video;
using RenderHeads.Media.AVProVideo;

public class UDPServer : MonoBehaviour
{
    public static UDPServer _instance;

    private void Awake()
    {
        _instance = this;
    }

    /// <summary>
    /// 模拟按键  按键对应表:http://www.doc88.com/p-895906443391.html
    /// </summary>
    /// <param name="bvk">虚拟键值 </param>
    /// <param name="bScan">0</param>
    /// <param name="dwFlags">0为按下,1按住,2释放</param>
    /// <param name="dwExtraInfo">0</param>
    [System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "keybd_event")]
    public static extern void Keybd_event(byte bvk, byte bScan, int dwFlags, int dwExtraInfo);


    //控制键盘事件
    public void KeybdWait(byte t)
    {
        Keybd_event(t, 0, 0, 0);
        Keybd_event(t, 0, 2, 0);
    }

    public string ipAddress = "192.168.0.10";
    public int ConnectPort = 8888;
    public string recvStr;

    public ReadConfigJson configJson;
    public VideoPlayer videoPlayer;

    public MediaPlayer mediaPlayer;
    public SoundManager soundManager;

    Socket socket;
    EndPoint clientEnd;
    IPEndPoint ipEnd;
    string sendStr;
    byte[] recvData = new byte[1024];
    byte[] sendData = new byte[1024];
    int recvLen;
    Thread connectThread;
    bool isPlayer,baohu=true;
    //初始化
    void InitSocket()
    {
        ipEnd = new IPEndPoint(IPAddress.Parse(ipAddress), ConnectPort);
        socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        socket.Bind(ipEnd);

        //定义客户端
        IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
        clientEnd = (EndPoint)sender;
        print("等待连接数据");
        //开启一个线程连接
        connectThread = new Thread(new ThreadStart(SocketReceive));
        connectThread.Start();

        
    }
   public void SocketSend(string sendStr)
    {
        sendData = new byte[1024];
        sendData = Encoding.UTF8.GetBytes(sendStr);
        socket.SendTo(sendData, sendData.Length, SocketFlags.None, clientEnd);
    }
    //服务器接收
    void SocketReceive()
    {
        while (true)
        {
            recvData = new byte[1024];
            recvLen = socket.ReceiveFrom(recvData, ref clientEnd);
            recvStr = Encoding.UTF8.GetString(recvData, 0, recvLen);
            Debug.Log("收到得信息 " + recvStr);
            //控制object要放在主线程里  控制系统虚拟按键要放在线程里——切记切记
            if (recvStr == "F5")
            {
                //recvStr = "";
                KeybdWait(116);
            }
            else if (recvStr == "prve")
            {
                //recvStr = "";
                KeybdWait(37);
            }
            else if (recvStr == "next")
            {
                //recvStr = "";
                KeybdWait(39);
            }
            else if (recvStr == "frist")
            {
               // recvStr = "";
                WindowMod._instance.SetFrist("中控播放器");
            }
            //else if (recvStr == "stop"|| recvStr.Contains(".mp4"))
            //{
            //    KeybdWait(27);
            //}
        }
    }

    //连接关闭
    void SocketQuit()
    {
        //关闭线程
        if (connectThread != null)
        {
            connectThread.Interrupt();
            connectThread.Abort();
        }
        //最后关闭socket
        if (socket != null)
            socket.Close();
        Debug.LogWarning("断开连接");
    }

    // Use this for initialization
    void Start()
    {
        ipAddress = IPManager.GetIP(ADDRESSFAM.IPv4);
        ConnectPort = configJson.configInfo.port;
        InitSocket(); //在这里初始化server
        ToolControlTaskBar.HideTaskBar();
    }

    public void PlayerInstruction()
    {
       
        if (recvStr == "frist")
        {
            recvStr = "";
            WindowMod._instance.SetFrist("ZKPlayer");
            KillProcess("PowerPoint 幻灯片放映 - [ppt1.pptx]");
            
        }
        else if (recvStr.Contains(".ppsx")&&baohu)
        {
            baohu = false;
            isPlayer = false;
            KillProcess("POWERPNT.EXE");
            FunctionControl._instance.shiping.SetActive(false);
            //videoPlayer.Stop();
            //videoPlayer.targetTexture.Release();
            mediaPlayer.Stop();
            Application.OpenURL(Application.streamingAssetsPath + "/ppt/" + recvStr);
            //StartCoroutine(WaitLookForPPT());
            //WindowMod._instance.SetFrist(recvStr+" - PowerPoint");
            recvStr = "";
            baohu = true;
        }
        else if (recvStr.Contains(".mp4"))
        {
            isPlayer = true;
            mediaPlayer.m_VideoPath = Application.streamingAssetsPath + "/mp4/" + recvStr;
            //mediaPlayer.
            //videoPlayer.url = Application.streamingAssetsPath + "/mp4/" + recvStr;
            //videoPlayer.Play();
            mediaPlayer.OpenVideoFromFile(MediaPlayer.FileLocation.RelativeToStreamingAssetsFolder, mediaPlayer.m_VideoPath);
            mediaPlayer.Play();
            recvStr = "";
            FunctionControl._instance.shiping.SetActive(true);
            WindowMod._instance.SetFrist("ZKPlayer");
            KillProcess("POWERPNT.EXE");
        }
        else if (recvStr == "play"&&isPlayer)
        {
            recvStr = "";
            //videoPlayer.Play();
            mediaPlayer.Play();
        }
        else if (recvStr == "pause")
        {
            //videoPlayer.Pause();
            mediaPlayer.Pause();
            recvStr = "";
        }
        else if (recvStr == "stop")
        {
            isPlayer = false;
            //videoPlayer.Stop();
            //videoPlayer.targetTexture.Release();
            mediaPlayer.Stop();
            WindowMod._instance.SetFrist("ZKPlayer");
            FunctionControl._instance.shiping.SetActive(false);
            KillProcess("POWERPNT.EXE");
            recvStr = "";
        }

            //else if (recvStr!=null && recvStr.Contains(".mp4"))
            //{
            //    videoPlayer.url= Application.streamingAssetsPath + "/mp4/" + recvStr;
            //    videoPlayer.Stop();
            //    videoPlayer.Play();
            //    recvStr = "";
            //}

      

        if (recvStr == "volumeup")
        {
            soundManager.SystemVolumeUp();
            recvStr = "";
        }
        else if (recvStr == "volumedown")
        {
            soundManager.SystemVolumeDown();
            recvStr = "";
        }
        else if (recvStr == "close")
        {
            CloseComputer();
            recvStr = "";
        }

    }

    private void Update()
    {
        PlayerInstruction();
        if (Screen.width != ReadConfigJson._instance.configInfo.screenWidth)
        {
            Screen.SetResolution(ReadConfigJson._instance.configInfo.screenWidth, ReadConfigJson._instance.configInfo.screenHeigth, true);
        }
    }

    void OnApplicationQuit()
    {
        SocketQuit();
        ToolControlTaskBar.ShowTaskBar();
    }

    public void CloseComputer()
    {
        System.Diagnostics.Process p = new System.Diagnostics.Process();
        p.StartInfo.FileName = "cmd.exe";
        p.StartInfo.Arguments = "/c " + "shutdown -s -t 0";
        p.Start();
    }

    /// <summary>
    /// 杀死进程
    /// </summary>
    /// <param name="processName">应用程序名</param>
    void KillProcess(string processName)
    {
        System.Diagnostics.Process[] processes = System.Diagnostics.Process.GetProcesses();
        foreach (System.Diagnostics.Process process in processes)
        {
            Debug.Log(process.ProcessName);
            try
            {
                if (!process.HasExited)
                {
                    if (process.ProcessName == processName)
                    {
                        process.Kill();
                        UnityEngine.Debug.Log("已杀死进程");
                    }
                }
            }
            catch (System.InvalidOperationException)
            {
                //UnityEngine.Debug.Log("Holy batman we've got an exception!");
            }
        }
    }


}

本来视频播放用的是unity自带组件videoplayer,但是展厅的主机没有独立显卡,视频播放起来会卡顿,于是便换成了AVpro。


3.电脑主机的开关机控制

开机:通过网络给电脑发开机指令
注意事项:你的电脑允许网络唤醒,在boss主板设置勾选允许网络唤醒
网卡驱动也要设置允许网络唤醒
在这里插入图片描述

如果你发现你的你没有电源管理,请更新网卡驱动

网络唤醒代码

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
public class WakeUpComputer : MonoBehaviour
{
    //通过正则表达式设定MAC地址筛选标准,关于正则表达式请自行百度
    const string macCheckRegexString = @"^([0-9a-fA-F]{2})(([/\s:-][0-9a-fA-F]{2}){5})$";
    private static readonly Regex MacCheckRegex = new Regex(macCheckRegexString);
    //唤醒主要逻辑方法
    public static bool WakeUp(string mac)
    {
        //查看该MAC地址是否匹配正则表达式定义,(mac,0)前一个参数是指mac地址,后一个是从指定位置开始查询,0即从头开始
        if (MacCheckRegex.IsMatch(mac, 0))
        {
            byte[] macByte = FormatMac(mac);
            WakeUpCore(macByte);
            return true;
        }
        return false;
    }
    private static void WakeUpCore(byte[] mac)
    {
        //发送方法是通过UDP
        UdpClient client = new UdpClient();
        //Broadcast内容为:255,255,255,255.广播形式,所以不需要IP
        client.Connect(IPAddress.Broadcast, 50000);
        //下方为发送内容的编制,6遍“FF”+17遍mac的byte类型字节。
        byte[] packet = new byte[17 * 6];
        for (int i = 0; i < 6; i++)
            packet[i] = 0xFF;
        for (int i = 1; i <= 16; i++)
            for (int j = 0; j < 6; j++)
                packet[i * 6 + j] = mac[j];
        //唤醒动作
        client.Send(packet, packet.Length);
    }
    private static byte[] FormatMac(string macInput)
    {
        byte[] mac = new byte[6];
        string str = macInput;
        //消除MAC地址中的“-”符号
        string[] sArray = str.Split('-');
        //mac地址从string转换成byte
        for (var i = 0; i < 6; i++)
        {
            var byteValue = Convert.ToByte(sArray[i], 16);
            mac[i] = byteValue;
        }
        return mac;
    }
    public void Button_Click_WakeUp(string s)
    {
        WakeUp(s);
        //print(WakeUp("4A-BB-6D-61-75-AE"));
        //print(WakeUp("E4-3A-6E-36-38-AA"));
    }
}

关机:发送指令给电脑关机
注意事项:关机没啥主意事项,很简单
关机代码:

 public void CloseComputer()
    {
        System.Diagnostics.Process p = new System.Diagnostics.Process();
        p.StartInfo.FileName = "cmd.exe";
        p.StartInfo.Arguments = "/c " + "shutdown -s -t 0";
        p.Start();
}

4.控制电视机的开关

开关机:RS232红外学习模块+串口服务器,
实现:串口服务器做服务端,RS232红外学习模块与串口服务器直连,自己开发客户端通过网络协议(我这里是UDP和端口号)连接到串口服务器上,发送指令给服务端,从而控制RS232红外学习模块。
(连接方式和设置同智能照明模块一样,接线可能有所区别)
注意事项:红外学习模块学习电视机的开关机频率,同时设置指令;
学习的时候,电视遥控器对准红外学习指示灯,按住遥控器开机键,直到红外学习指示灯 快速连闪3下,学习结束

  • 18
    点赞
  • 96
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
### 回答1: Unity是一款跨平台的游戏引擎,被广泛应用于游戏开发和虚拟现实(VR)、增强现实(AR)应用的开发。也可以用于开发展厅控系统。 在Unity开发控系统过程,需要考虑以下几方面的内容: 1. 界面设计:展厅控系统的界面设计需要符合用户习惯,能够方便快速地控制和操作展品,同时美观大方。 2. 数据管理:展品信息和状态需要通过数据管理模块进行统一管理,保证系统的可靠性和稳定性。 3. 操作指引:针对不同展品的操作指引应该清晰明了,能够帮助用户快速上手。 4. 系统测试:在开发过程需要进行充分的测试,确保系统稳定可用。 关于如何实现上述内容,可以参考Unity官方文档和教程,并结合实际需求进行开发。需要注意的是,在实际应用可能会面临不同的场景和需求,需要根据实际情况进行优化和调整。\ 总之,通过合理的界面设计、数据管理、操作指引和系统测试,基于Unity展厅控系统可以快速高效地实现,为用户带来更好的展品浏览和体验效果。 ### 回答2: Unity开发展厅控系统是为了方便展厅心控制和管理而设计的一款软件系统,其教程在市面上也有很多的资源可以参考。 首先,在使用Unity开发展厅控系统之前,需要先学习基本的Unity知识,并掌握相关的编程语言。特别是针对展厅心控制的需求,还需要了解一些数据处理和服务器通信等方面的知识。 其次,在编写展厅控系统时,需要注意以下几点: 1. 界面设计:展厅控系统的界面设计需要符合整体风格,易于操作,能够简明地显示相关信息,并提供灵活的交互方式。 2. 数据处理:展厅控系统需要处理多种不同类型的数据,包括实时数据、历史数据、控制指令等等。需要根据实际需求,设计合适的数据结构和处理算法,同时保证数据的准确性和安全性。 3. 服务器通信:展厅控系统需要和服务器进行通信,获取数据或发送指令。这需要考虑网络延迟、数据传输的安全等问题,同时还需要设计合适的通信协议,保障系统的稳定运行。 最后,值得注意的是,不同展厅控系统需求可能会有所不同,因此在使用Unity开发展厅控系统前,需要事先进行需求分析,并根据实际情况进行相应的定制化开发。同时,不断地进行优化和改进,以提高终端用户的体验,并确保系统的稳定性和可靠性。 ### 回答3: Unity开发展厅控系统教程是一种非常有用的技能。通过掌握这种技能,开发者可以创建出各种智能化的、具有交互性的控系统。下面就是这种技能的一些教程步骤。 首先,开发者需要了解如何在Unity插入模型和UI。这包括将模型导入Unity、创建UI组件等。 其次,开发者需要掌握如何将控系统各个界面之间进行跳转、如何实现按钮或其他UI元素的交互功能等。 第三,了解如何通过Unity实现数据存储和读取功能。比如将某个房间的灯光状态保存到本地或数据库,并在需要时读取回来。 第四,掌握如何通过Unity的Networking库将控系统和服务器进行连接,实现远程控制的功能。 最后,进行测试和优化。在开发过程开发者需要不断进行测试和优化,保证控系统的流畅性和稳定性。 总之,通过这些教程步骤,开发者可以轻松地掌握Unity开发展厅控系统的技能,以实现智能化的、具有交互性的控系统

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值