2015-11-17 17:55:35 xxdddail 阅读数 12364
  • Unity3D入门到精通-(3)Unity资源管理精讲

    本次系列课程的目标是让Unity3D初学者掌握Unity3d的资源管理技术进行了全面介绍,特别对AssetBundle资源如何进行更新,以及加载(依赖资源加载)进行了系统的介绍。 适合对象:Unity初学开发者,Unity中级开发者,网络程序开发者,所有对游戏开发有兴趣的人员。 学习条件:有一定的Unity3D基础,了解C#的基本开发知识。

    4645 人正在学习 去看看 张刚

Unity3D可以C#脚本进行开,使用vstu2013.msi插件,可以实现在VS2013中的调试。在开发完成后,由于项目需要,需要将Unity3D嵌入到WinForm中。WinForm中的UnityWebPlayer Control可以载入Unity3D。先看效果图。


一、为了能够动态设置axUnityWebPlayer的Src,我使用用户控件来封装。看下面的代码。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;

namespace UnityHost
{
    public partial class U3DPlayer : UserControl, IMessageFilter
    {
        #region 属性
        private String _src;
        /// <summary>
        /// Unity3D文件的路径
        /// </summary>
        public String Src
        {
            get { return _src; }
            private set { _src = value; }
        }

        private bool _disableMouseRight = true;
        /// <summary>
        /// 禁用鼠标右键
        /// </summary>
        public bool DisableMouseRight
        {
            get { return _disableMouseRight; }
            set { _disableMouseRight = value; }
        }

        #endregion

        #region 自定义事件
        //委托
        public delegate void ExternalCallHandler(object sender, AxUnityWebPlayerAXLib._DUnityWebPlayerAXEvents_OnExternalCallEvent e);
        /// <summary>
        /// 接收Unity调用宿主函数的消息
        /// </summary>
        [Browsable(true), Description("接收Unity调用宿主(如WinForm)函数的消息")]
        public event ExternalCallHandler UnityCall;
        //方法
        public void OnUnityCall(object sender, AxUnityWebPlayerAXLib._DUnityWebPlayerAXEvents_OnExternalCallEvent e)
        {
            if (UnityCall != null)
            {
                UnityCall(sender, e);
            }
        }
        #endregion

        #region 内部变量
        private AxUnityWebPlayerAXLib.AxUnityWebPlayer _axUnityWebPlayer=null;
        private ProgressBar _progressBarLoad=null;
        #endregion

        public U3DPlayer()
        {
            InitializeComponent();
            InitProgressBar();
        }

        private void InitProgressBar()
        {
            if (_progressBarLoad == null)
            {
                _progressBarLoad = new ProgressBar();
                _progressBarLoad.Height = 100;
                _progressBarLoad.Style = ProgressBarStyle.Marquee;
                _progressBarLoad.Top = (this.Height - _progressBarLoad.Height) / 2;
                Controls.Add(_progressBarLoad);
            }
        }

        #region InitUnity
        /// <summary>
        /// 初始化UnityWebPlayer
        /// </summary>
        /// <param name="src">Unity3D文件的路径</param>
        public void InitUnity(String src)
        {
            Src = src;
            if (!File.Exists(Src))
            {
                return;
            }
            var unity = new AxUnityWebPlayerAXLib.AxUnityWebPlayer();
            ((System.ComponentModel.ISupportInitialize)(unity)).BeginInit();
            Controls.Add(unity);
            ((System.ComponentModel.ISupportInitialize)(unity)).EndInit();
            unity.src = Src;//Application.StartupPath + "\\u.unity3d";  //改成自己想要的路径
            AxHost.State state = unity.OcxState;
            Controls.Remove(unity);
            unity.Dispose();
            unity = new AxUnityWebPlayerAXLib.AxUnityWebPlayer();
            ((System.ComponentModel.ISupportInitialize)(unity)).BeginInit();
            this.SuspendLayout();
            unity.Dock = DockStyle.Fill;
            //unity.Name = "Unity";
            unity.OcxState = state;
            unity.TabIndex = 0;
            this.Controls.Add(unity); //panel1是我用的一个容器,改成this.Controls也可以
            ((System.ComponentModel.ISupportInitialize)(unity)).EndInit();
            this.ResumeLayout(false);
            _axUnityWebPlayer = unity;
            if (_axUnityWebPlayer == null)
            {
                throw new Exception("_axUnityWebPlayer init fail");
            }
            else
            {
                _axUnityWebPlayer.OnExternalCall += _axUnityWebPlayer_OnExternalCall;
                _axUnityWebPlayer.Hide();
                ShowProgressBar();
            }
        }
        #endregion

        #region 进度条
        private void ShowProgressBar()
        {           
            _progressBarLoad.Visible = true;
            _progressBarLoad.Left = 0;
            _progressBarLoad.Width = this.Width;
        }

        private void HideProgressBar()
        {
            if (_progressBarLoad!=null)
            {
                _progressBarLoad.Visible = false;    
            }            
        }
        #endregion

        void _axUnityWebPlayer_OnExternalCall(object sender, AxUnityWebPlayerAXLib._DUnityWebPlayerAXEvents_OnExternalCallEvent e)
        {
            if (e.value.StartsWith("LOAD_COMPLETE"))
            {
                if (!_axUnityWebPlayer.Visible)
                {
                    _axUnityWebPlayer.Width = this.Width;
                    _axUnityWebPlayer.Height = this.Height;
                    _axUnityWebPlayer.Show();
                    HideProgressBar();
                }
            }
            OnUnityCall(sender, e);
        }


        private void U3DPlayer_Load(object sender, EventArgs e)
        {
            Graphics g = this.CreateGraphics();
            g.Clear(this.BackColor);

            if (DisableMouseRight)
            {
                Application.AddMessageFilter(this);
                this.Disposed += U3DPlayer_Disposed;
            }
        }

        void U3DPlayer_Disposed(object sender, EventArgs e)
        {
            if (DisableMouseRight)
            {
                Application.RemoveMessageFilter(this);
            }
        }

        #region SendMessage
        /// <summary>
        /// 发送消息给Unity
        /// </summary>
        /// <param name="unityObjName">Unity中的对象名称</param>
        /// <param name="unityScriptyMethod">Unity脚本中的方法</param>
        /// <param name="val">传送的值.仅限于int、float、string</param>
        public void SendMessage(string unityObjName, string unityScriptyMethod, object val)
        {
            if (_axUnityWebPlayer == null)
            {
                return;
            }
            _axUnityWebPlayer.SendMessage(unityObjName, unityScriptyMethod, val);
        }
        #endregion

        private void U3DPlayer_MouseDown(object sender, MouseEventArgs e)
        {

        }

        /// <summary>
        /// 过滤鼠标右键
        /// </summary>
        /// <param name="m"></param>
        /// <returns></returns>
        public bool PreFilterMessage(ref System.Windows.Forms.Message m)
        {
            if (_axUnityWebPlayer == null)
            {
                return false;
            }
            const int WM_RBUTTONDOWN = 0x204;
            const int WM_RBUTTONUP = 0x205;
            const int WM_RBUTTONDBLCLK = 0x206;
            // 屏蔽右键消息区域。
            System.Drawing.Rectangle my_Area = new System.Drawing.Rectangle(_axUnityWebPlayer.Location, _axUnityWebPlayer.Size);

            if (my_Area.Contains(this.PointToClient(Control.MousePosition)))
            {
                switch (m.Msg)
                {
                    case WM_RBUTTONDOWN:
                        return true;
                    case WM_RBUTTONUP:
                        return true;
                    case WM_RBUTTONDBLCLK:
                        return true;
                    default:
                        return false;
                }
            }

            return false;
        }

    }
}
注:代码中还实现了其他的功能,如下

1.增加InitUnity方法,方便外层控件调用。这里最关键的是OcxState,必须使用AxUnityWebPlayer才能依据Src动态产生。

2.动态增加进度条。

3.在实始化后对_axUnityWebPlayer进行隐藏,同时启动进度条,并绑定Unity的回调事件OnExternalCall。在OnExternalCall事件中,监听Unity发来的LOAD_COMPLETE值,然后判断是否显示_axUnityWebPlayer.

4.为了能让外层也收到Unity发来的消息,使用委托二次实现了OnExternalCall,也就是OnUnityCall方法。

5.SendMessage的实现,第一个参数为Unity中的对象名称,第二个参数为Unity脚本中的方法,第三个参数是传送的值(仅限于int、string,其他的会失败或者异常)。

6.继承IMessageFilter接口,捕获消息,然后过滤_axUnityWebPlayer区域内产生的鼠标右键消息,同时增加DisableMouseRight属性来控制。

7.一定要将项目调成x86的模式,否则会报“没有注册类XXX”的信息。

8.axUnityWebPlayer控件需要在工具箱中添加,如下图。


二、窗体界面的代码

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;

namespace UnityHost
{
    public partial class FormHost : Form
    {
        public FormHost()
        {
            InitializeComponent();
        }

        private void buttonSendToUnity_Click(object sender, EventArgs e)
        {
            String info = textBoxSendMessage.Text;
            if (String.IsNullOrWhiteSpace(info))
            {
                MessageBox.Show("请输入内容");
                return;
            }
            u3DPlayer1.SendMessage("Main Camera", "CallUnity", info);
        }

        private void FormHost_Load(object sender, EventArgs e)
        {
            String src = Application.StartupPath + "\\UnityWeb\\UnityWeb.unity3d";
            u3DPlayer1.InitUnity(src);
        }

        private void u3DPlayer1_UnityCall(object sender, AxUnityWebPlayerAXLib._DUnityWebPlayerAXEvents_OnExternalCallEvent e)
        {
            this.Text = "收到Unity的消息:" + e.value;
        }
    }
}
三、Unity3D的C#脚本

using UnityEngine;
using System.Collections;
using System;

public class Main : MonoBehaviour
{

    private string _messageReceive = string.Empty;
    private bool _isButtonClick = false;
    private int _notifyTimeAfterLoadComplete = 3;

    // Use this for initialization
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {

    }

    void OnGUI()
    {
        if (GUI.Button(new Rect(100, 10, 80, 20), "测试"))
        {
            _isButtonClick = !_isButtonClick;
        }

        GUI.Label(new Rect(50, 30, 150, 30), _messageReceive);

        if (_isButtonClick)
        {
            Application.ExternalCall("ToWinform", Guid.NewGuid().ToString());
            _isButtonClick = false;
        }

        if (_notifyTimeAfterLoadComplete>0)
        {
            Application.ExternalCall("LOAD_COMPLETE", "");
            _notifyTimeAfterLoadComplete--;
        }
    }

    void CallUnity(object val)
    {
        _messageReceive = string.Format("{0}", val);
    }
}
注:

1.CallUnity是响应WinForm发来消息的函数。

2.Application.ExternalCall是向WinForm发出消息,第一参数是函数的名称,第二个之后的参数是函数的参数。

四、Unity3D要在WebPlayer模式下编译


转载请注明出处

代码下载http://download.csdn.net/detail/xxdddail/9277447


2018-11-30 15:46:58 qq_18941713 阅读数 1382
  • Unity3D入门到精通-(3)Unity资源管理精讲

    本次系列课程的目标是让Unity3D初学者掌握Unity3d的资源管理技术进行了全面介绍,特别对AssetBundle资源如何进行更新,以及加载(依赖资源加载)进行了系统的介绍。 适合对象:Unity初学开发者,Unity中级开发者,网络程序开发者,所有对游戏开发有兴趣的人员。 学习条件:有一定的Unity3D基础,了解C#的基本开发知识。

    4645 人正在学习 去看看 张刚

前言

大概是去年还没开学的时候,提前到校,给老师干活拿到的需求,可能也跟我说对unity3D有兴趣有关系吧。总之就是unity3D做模拟环境,但是呢,要嵌入要其他的软件中去使用,窗口环境,也就是题目上的那几种,Winform、MFC。
先写一写印象中的思路,代码还要再找找…电脑数据丢过,不太好找到了。
就在这个时候…我翻了一下聊天记录,梳理了思路。

unity嵌入Winform

在winform中可以嵌入unity专用插件
在这里插入图片描述

unity相对路径的设置

大家都知道,在插件初始化的时候,会直接加载完成这个插件的内容,也就是说,如果直接用插件本身去读取unity,需要使用绝对路径,这样就无法实现分布式了,肯定是不能直接提交给上级的。
所以,思路大概是这样的,首先在界面中加入一个窗口的控件,在这个空间里加载一个UnityWebPlayer Control,由于是在空白控件上生成一个新的控件,所以可以在代码中对unity路径进行设置,也就实现了相对路径。
也可借鉴WinFrom内嵌Unity3D
具体代码找不到了…只有一部分PPT讲解时候的记录

private void InitUnity()
{
    var unity = new AxUnityWebPlayerAXLib.AxUnityWebPlayer();
    ((System.ComponentModel.ISupportInitialize)(unity)).BeginInit();
    Controls.Add(unity);
    ((System.ComponentModel.ISupportInitialize)(unity)).EndInit();
    unity.src = Application.StartupPath + "\\u.unity3d";  //改成自己想要的路径
    AxHost.State state = unity.OcxState;
    unity.Dispose();
    unity = new AxUnityWebPlayerAXLib.AxUnityWebPlayer();
    (System.ComponentModel.ISupportInitialize)(unity)).BeginInit();
    this.SuspendLayout();
    unity.Dock = DockStyle.Fill;
    unity.Name = "Unity";
    unity.OcxState = state;
    unity.TabIndex = 0;
    this.Controls.Add(unity); 
    ((System.ComponentModel.ISupportInitialize)(unity)).EndInit();
    this.ResumeLayout(false);
}

unity与winform的通讯过程

由于嵌入winform的是unity的专用控件,所以通讯方式也是内部直接写好的

//winfrom向unity的名为air的gameobject的Channel方法,发送task消息
private void button_Click(object sender, EventArgs e)
{
    axUnityWebPlayer1.SendMessage("air", "Channel", task);
}
//unity接收winform发给Channel方法的消息string c
public void Channel(string c) {
    StartCoroutine(GetRoutine(c));
}
//unity向winform中unity控件的LOAD_COMPLETE方法发送""消息
    Application.ExternalCall("LOAD_COMPLETE", "");
//winform接收unity发来的消息
//e.value == "LOAD_COMPLETE()"
//e.value.StartsWith("LOAD_COMPLETE") == true
private void axUnityWebPlayer1_OnExternalCall(object sender, AxUnityWebPlayerAXLib._DUnityWebPlayerAXEvents_OnExternalCallEvent e)
{
    this.label1.Text = e.value;
}

unity嵌入MFC

这里由于UnityWebPlayer Control并不兼容MFC,所以需要借用另外的方式来实现这一需求,UnityWebPlayer原本就是为网页游戏提供的一种插件,自然可以在网页控件上实现,这里我在MFC中加入web browser也就是浏览器的意思。unitywebplayer在导出的时候,会有html、js、unity3d等多个文件,html是静态文件,所以选用js来跟unity做交互,通过js对unity、mfc的消息进行转发,来达到unity与mfc互通的目标。

unity→js→mfc

//unity
Application.ExternalCall("SayHello_From_Unity", "The game says hello!");
//js
function SayHello_From_Unity(args) {
			alert(args);
			o.ShowMessageBox("~~~");
}
//mfc
void CRobotteach::ShowMessageBox(const wchar_t *msg)
{
	MessageBox(msg, L"这是来自javascript的消息");
}

mfc→js→unity

//mfc
//m_unity是一个WebBrowser的Activex控件对象。  
CComQIPtr<IHTMLDocument2> spDoc = m_unity.get_Document();
CComDispatchDriver spScript;
spDoc->get_Script(&spScript);

//代码更新于VS2017
CComVariant var1 = 10, var2 = 20, varRet;
spScript.Invoke2(L"Add", &var1, &var2, &varRet);

//代码更新于VS2015
CString hello = L"Hello--MFC";
VARIANT res = _variant_t(hello);
HRESULT hr = spScript.Invoke1(L"SayHello_From_MFC",&res);
//js
function SayHello_From_MFC(args) {
	alert(args);
	u.getUnity().SendMessage("arm", "Hello", "Hello from a web page!");
}
//unity
public void Hello(string args) {
    DebugConsole.Log(args);
}

js主动向unity,是需要一定基础的,在这篇文章中可以找到答案。

2016-06-13 13:39:31 IOTHua 阅读数 9443
  • Unity3D入门到精通-(3)Unity资源管理精讲

    本次系列课程的目标是让Unity3D初学者掌握Unity3d的资源管理技术进行了全面介绍,特别对AssetBundle资源如何进行更新,以及加载(依赖资源加载)进行了系统的介绍。 适合对象:Unity初学开发者,Unity中级开发者,网络程序开发者,所有对游戏开发有兴趣的人员。 学习条件:有一定的Unity3D基础,了解C#的基本开发知识。

    4645 人正在学习 去看看 张刚

前言

1、有些人其实会觉得Unity3D用到的.NET是2.0的,其实不然;Unity3D有用到.NET3.5,为什么说Unity用到的是3.5呢,从一个很常用却很重要的一个命名空间说起,他就是System.Linq命名空间,这个命名空间是.NET3.5重要的一次改革和核心部分(本命名空间与该文章并没有什么很大的联系,只是提下而已)。至于为什么显示成2.0我也不是很清楚,可能只支持部分3.5吧,不过对我们来说关系并不是很大。只要支持Linq就可以了。

2、前提工作:虚拟串口和Unity3D切换成.NET。
2.1 虚拟串口的创建,可以从网上下载一个创建虚拟串口的软件,比如“VSPD虚拟串口”,还是挺好用的,不过因为我做Unity3D的虚拟串口工作,所以根据VSPD专门写了一个创建虚拟串口的程序(暂时不提供)。在创建虚拟串口的时候注意一个很重要的问题,就是尽量创建串口号大于10的,比如COM10、COM11甚至夸张点COM100等,为什么要这样子,后面我会介绍Unity3D打开串口时,串口号大于10时,打开串口方式与.NET打开串口的方式是不一样的。
2.2 将Unity3D的API平台切换成.NET2.0。如何切换“Edit–>project Setting–>Player–>Other Setting –>Api Compatibility level”。在这里将“.NET2.0 Subset”切换为“.NET2.0”

切换.NET2.0
2.3 Unity的目标平台一定要切换为Windows平台,否则是其他平台会报错误,本人就是深有体会,针对这个问题找原因找了很久,什么百度、谷歌、论坛都查阅了,最后还是无意中自己发现解决的了。

平台切换
切换为Web平台时报的错误
Web平台
3、Unity的串口与.NET的串口对象参数有些不一样,比如在Unity3D中打开串口,SerialPort对象的属性、方法、事件等要比.NET SerialPort对象的属性、事件、方法要少一些。(图片不能显示,所以不就贴图了,只是说明下情况),甚至Unity3D的有些属性还是错误的,比如BytesToRead和BytesToWrite两个属性都是“未将对象引用值对象的实例”,但是在.NET中这两个参数默认是为0。这两个参数用于接收串口发送字节数组时,是很有用处的。
这是Unity中串口对象里的属性
Unity中串口对象里的属性
这是WinForm中串口对象里的属性
WinForm中串口对象里的属性
4、虚拟串口的创建,不像是真实串口线那样子,它是以对来创建的,比如COM100与COM101一对……至于怎么成对完全是有那个创建虚拟串口的软件以及你输入的串口来决定的。
设备管理器中串口号

一、Unity3D内部串口通信

1、内部通信思路
1.1 打开串口
之前在前言中说过,Unity打开串口方式不一样,因为在.NET2.0打开串口时,如果串口超过10,则必须在前面加上“\\?\”,比如我需要打开COM301,在Unity中你实际传给串口的参数必须是“”\\?\” + “COM301””。
在命名空间中引用System.IO.Ports
创建两个串口类对象

private SerialPort gatewayPort, coorPort;
//分别是网关串口和协调器串口,命名的话随自己。
    然后写一个打开串口方法
    注意:下面的网关串口和协调器串口只是我的命名而已,其实串口之间的通信。
    **打开网关串口**
        gatewayPort = new SerialPort("\\\\?\\" + "COM301", 9600);
        gatewayPort.ReadTimeout = 500;
        gatewayPort.Open();
        Debug.Log("网关串口打开成功");
        //用于接收协调器串口发送过来的数据
        tgateway = new Thread(ReceivePortThread2);
        tgateway.IsBackground = true;
        tgateway.Start();
    *打开协调器串口*
        coorPort = new SerialPort("\\\\?\\" + "COM3301", 9600);
        coorPort.ReadTimeout = 500;
        coorPort.Open();
        Debug.Log("协调器串口打开成功");
        //用于接收网关串口发送过来的数据
        tcoor = new Thread(ReceivePortThread1);
        tcoor.IsBackground = true;
        tcoor.Start();

1.2 线程接收数据
两个串口接收数据,并且打印出来,一般接收数据的方法常用的有两种,一种是接收字符串ReadLine()另一种接收字节Read,稍微我会将接收字节已注释的形式写出来。

字节接收的方式,把代码放在网关接收方法里和协调器方法里,根据个人需求吧
//byte[] buffer = new byte[1024];
 //int count = this.coorPort.Read(buffer, 0, buffer.Length);
 //if(count == 0) {
 //    continue;
 //}

 //byte[] bufferRead = new byte[count];

 //System.Array.Copy(buffer, 0, bufferRead, 0, count);
 //string strRec = ClassConvert.BytesToString(bufferRead);
 ClassConvert类是一些字节、字符串转化的方法,后面会提供

网关接收数据方法

    private void ReceivePortThread2()
    {
        while(true) {
            Thread.Sleep(1);
            if(this.gatewayPort != null && this.gatewayPort.IsOpen) {
                try {
                    string strRec = gatewayPort.ReadLine(); 
                    Debug.Log("网关读取数据:" + strRec);
                }
                catch {
                    //continue;
                }
            }
        }
    }

协调器接收数据方法

    private void ReceivePortThread1()
    {
        while(true) {
            Thread.Sleep(1);
            if(this.coorPort != null && this.coorPort.IsOpen) {
                try {
                    string strRec = coorPort.ReadLine(); 
                    Debug.Log("协调器读取数据:" + strRec);
                }
                catch {
                    //continue;
                }
            }
        }
    }

1.3 发送数据
将这下面两个方法分别加入到UI Button的事件中,具体如何加这里就不解释了。

    /// <summary>
    /// 网关
    /// </summary>
    public void OnGateWay()
    {
        this.gatewayPort.DiscardOutBuffer();
        gatewayPort.WriteLine("FF0000");

        //byte[] buffer = new byte[] { 0xFF, 0x00, 0x01 };
        //gatewayPort.Write(buffer, 0, buffer.Length);
    }

    /// <summary>
    /// 协调器
    /// </summary>
    public void OnCoor()
    {
        this.coorPort.DiscardOutBuffer();
        coorPort.WriteLine("00FFFF");
        //byte[] buffer = new byte[] { 0x00, 0xFF, 0xFE };
        //coorPort.Write(buffer, 0, buffer.Length);
    }

2、代码
主要类PortsTest.cs,字节字符串转化类ClassConvert.cs

using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System.IO.Ports;
using System.Threading;

public class PortsTest : MonoBehaviour {

    private SerialPort gatewayPort, coorPort;

    public Thread tgateway, tcoor;

    // Use this for initialization
    void Start () {
        GateWayOpen();
        CoorOpen();
    }

    // Update is called once per frame
    void Update () {

    }

    void GateWayOpen()
    {
        gatewayPort = new SerialPort("\\\\?\\" + "COM301", 9600);
        gatewayPort.ReadTimeout = 500;
        gatewayPort.Open();
        Debug.Log("网关串口打开成功");

        tgateway = new Thread(ReceivePortThread2);
        tgateway.IsBackground = true;
        tgateway.Start();

    }

    void CoorOpen()
    {
        coorPort = new SerialPort("\\\\?\\" + "COM3301", 9600);
        coorPort.ReadTimeout = 500;
        coorPort.Open();
        Debug.Log("协调器串口打开成功");

        tcoor = new Thread(ReceivePortThread1);
        tcoor.IsBackground = true;
        tcoor.Start();
    }

    /// <summary>
    /// 串口接收数据方法
    /// </summary>
    private void ReceivePortThread1()
    {
        while(true) {
            Thread.Sleep(1);

            if(this.coorPort != null && this.coorPort.IsOpen) {
                try {
                    //byte[] buffer = new byte[1024];
                    //int count = this.coorPort.Read(buffer, 0, buffer.Length);
                    //if(count == 0) {
                    //    continue;
                    //}

                    //byte[] bufferRead = new byte[count];

                    //System.Array.Copy(buffer, 0, bufferRead, 0, count);
                    //string strRec = ClassConvert.BytesToString(bufferRead);

                    string strRec = coorPort.ReadLine();   

                    Debug.Log("协调器读取数据:" + strRec);
                }
                catch {
                    //continue;
                }
            }
        }
    }

    /// <summary>
    /// 串口接收数据方法
    /// </summary>
    private void ReceivePortThread2()
    {
        while(true) {
            Thread.Sleep(1);

            if(this.gatewayPort != null && this.gatewayPort.IsOpen) {
                try {
                    string strRec = gatewayPort.ReadLine();   
                    Debug.Log("网关读取数据:" + strRec);
                }
                catch {
                    //continue;
                }
            }
        }
    }

    /// <summary>
    /// 网关
    /// </summary>
    public void OnGateWay()
    {
        this.gatewayPort.DiscardOutBuffer();
        gatewayPort.WriteLine("FF0000");

        //byte[] buffer = new byte[] { 0xFF, 0x00, 0x01 };
        //gatewayPort.Write(buffer, 0, buffer.Length);
    }

    /// <summary>
    /// 协调器
    /// </summary>
    public void OnCoor()
    {
        this.coorPort.DiscardOutBuffer();
        coorPort.WriteLine("00FFFF");
        //byte[] buffer = new byte[] { 0x00, 0xFF, 0xFE };
        //coorPort.Write(buffer, 0, buffer.Length);
    }
}
using System;

public class ClassConvert
{
    /// <summary>
    /// 字节数据转字符串
    /// </summary>
    /// <param name="bytes"></param>
    /// <returns></returns>
    public static string BytesToString(byte[] bytes)
    {
        string result = "";
        foreach (byte b in bytes)
        {
            result = result + string.Format("{0:X2}", b);
        }
        return result;
    }

    /// <summary>
    /// 字节数据转字符串(带格式)
    /// </summary>
    /// <param name="bytes"></param>
    /// <returns></returns>
    public static string BytesToStringFormat(byte[] bytes)
    {
        string result = "";
        foreach (byte b in bytes)
        {
            result = result + string.Format("{0:X2}", b) + "-";
        }
        return result.Substring(0, result.Length - 1);
    }

    /// <summary>
    /// 2位字符串转字节
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    public static byte StringToByte(string str)
    {
        try
        {
            str = System.Convert.ToInt32(str, 16).ToString();
        }
        catch (Exception err)
        {
            throw err;
        }

        byte result = 0;
        if (byte.TryParse(str, out result) == true)
        {
            return result;
        }
        else
        {
            throw new Exception("StringToByte error");
        }
    }

    /// <summary>
    /// 字符串转字节数据
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    public static byte[] StringToBytes(string str)
    {
        byte[] result = new byte[str.Length / 2];
        for (int i = 0; i < str.Length; i = i + 2)
        {
            result[i / 2] = StringToByte(str.Substring(i, 2));
        }
        return result;
    }
}

3、运行结果和异常解析
运行程序后,会提示网关串口打开成功和协调器串口打开成功。
3.1、当以字符串形式发送串口数据和接收串口数据时,会发现一个问题就是在接收串口数据时,会出现数据丢失的情况,网关串口向协调器发送”FF0000”时,协调器接收数据偶尔会接收到“F0000”甚至是为空,只有当连续发送两次时,才会成功。
运行结果,数据出现丢失
3.2、当以字节发送和接收串口数据时,会出现一条完整的数据会以两次打印出来。比如将“new byte[] { 0xFF, 0x00, 0x01 }”发送过去,然后打印出来的结果是第一条是FF 第二条是00 01等等情况,感觉像是随机的。
运行结果
3、当以字节发送,字符串形式接收时,是无法接收数据的。

以上问题目前我也不知道是什么情况,解决思路是怎样的,发生该问题的原因可能是因为Unity对串口这块本身支持就不是很大,毕竟不是专门针对Windows平台的,如果有看到本文章的读者知道的话,请联系我的邮箱:1158078383@qq.com 或者微博:http://weibo.com/5062862016/profile?topnav=1&wvr=6&is_all=1 本人非常感谢!!

二、Unity3D与Winform程序之间的串口通信

在第一部分中介绍了Unity3D内部间的通信,现在测试Unity3D与Winform程序之间的串口通信。
首先Unity3D串口程序跟第一节类似的,只不过把网关打开串口那一部分代码移植到Winform中,然后修改一下打开串口的方式即可。
1、打开串口方式

private SerialPort gatewayPort;
public Thread tgateway;

gatewayPort = new SerialPort("COM202", 9600);
gatewayPort.ReadTimeout = 500;
gatewayPort.Open();

tgateway = new Thread(new ThreadStart(ReceivePortThread2));
tgateway.IsBackground = true;
tgateway.Start();

private void ReceivePortThread2()
{
    while(true) {
        Thread.Sleep(1);
        if(this.gatewayPort != null && this.gatewayPort.IsOpen) {
            try {
                string strRec = gatewayPort.ReadLine();  
                MessageBox.Show(strRec);
            }
            catch {
            }
        }
    }
}

以上就是核心代码。
2、遇到的问题
发送字符串和接收字符串遇到以下发生过的问题
2.1 winform程序发送数据成功了,但是Unity接收不到
2.2 Unity往Winform程序总发送数据时,是没有问题的。而Unity却接收不到。
Winform程序接收到了Unity串口发送过来的数据
发送字节和接收字节遇到以下发生过的问题
2.3 WinForm程序发送数据成功了,但是Unity接收到的数据存在问题,数据不符或数据中断,要想解决这个问题有两种方法:
第一可能是Unity官方的错误,如果能做成跟.NET串口通信一致的话,那么这个问题很好解决。不过这个问题不够现实,因为Unity本身就是为游戏而开发的。
第二那就自己去解决了,看到Unity接收到的数据存在数据不符,还有数据断层,只能根据自身的要求,然后去测试,添加校验位,根据首校验位和末校验位来截取你想要的字节。只有这样子你才可能接收到正常的串口数据。但是这样子也存在很多的局限性!!!

2.4 Unity往WinForm程序中发送的数据时,是没有问题的。
Unity接收到的数据和WinForm程序接收到的数据
WinForm 接收数据关键代码:

if(gatewayPort.BytesToRead > 0) {
     byte[] bufferRead = new byte[this.gatewayPort.BytesToRead];
     this.gatewayPort.Read(bufferRead, 0, bufferRead.Length);
     string strRec = ClassConvert.BytesToString(bufferRead);
}

WinForm发送数据关键代码:

gatewayPort.DiscardInBuffer();
byte[] buffer = new byte[] { 0xFF, 0x00, 0x01 };
gatewayPort.Write(buffer, 0, buffer.Length);

Unity的接收数据关键代码和发送数据关键代码已在第一节都贴出来了。

2015-01-16 20:44:33 llhswwha 阅读数 3502
  • Unity3D入门到精通-(3)Unity资源管理精讲

    本次系列课程的目标是让Unity3D初学者掌握Unity3d的资源管理技术进行了全面介绍,特别对AssetBundle资源如何进行更新,以及加载(依赖资源加载)进行了系统的介绍。 适合对象:Unity初学开发者,Unity中级开发者,网络程序开发者,所有对游戏开发有兴趣的人员。 学习条件:有一定的Unity3D基础,了解C#的基本开发知识。

    4645 人正在学习 去看看 张刚

今天工作需要,要把Unity3d嵌入WinForm中,具体怎么做网上都有教程的。

但是,按照教程来,即 在WinForm中添加UnityWebPlayer控件(这个是COM组件),结果无法正确显示。

此时界面为:

 

Error details是:

 

Src路径是.unity3d的全路径,路径正确的,如果路径错误的话,提示是:

 

注意此时我已经安装了UnityWebPlayer插件。(我搜索单机显示unity3d时,只找到说是要安装这个)

 

我工作的电脑是无法上网的,实际上以前在家里的时候按照教程,3d场景是能正确显示的。然后我把程序拿到上网机上实验(由于现在我还没用动态src代码,还要修改盘符以便路径正确,挺麻烦的)。发现上网机是可以的,只不过第一次加载很慢,很慢很慢!

而后把上网机断网,还是能打开。有希望了,并不是一定要联网。

 

后台肯定下载了什么东西了!

安装错误提示把UnityPlayer3.x.x-win32.zip下载下来了,但是里面不是安装包,都是些dll,根本不知道怎么用。

 

实际上我一开始怀疑IE临时文件里面有没有什么东西,但是找不到什么可疑的,copy出来文件名又会改变,放弃。

真不知道该怎么办了,没办法,搜索一下c盘中有什么和unity相关的吧,同时在注册表中也找一下(用unity搜),大概4次F3后 让我找到一个

"C:\\Users\\Administrator\\AppData\\LocalLow\\Unity\\WebPlayer\\loader\\UnityWebPluginAX.ocx"

HKEY_CLASSES_ROOT\CLSID\{444785F1-DE89-4295-863A-D46C3A781394}\InprocServer32

 

我的电脑上打开该目录,上升到WebPlayer文件夹,里面有

而上网机上打开WebPlayer,里面有

 

这个文件夹居然有50M,明显可疑!

 

把上网机上的copy到工作机上,打开,OK!可以了!!!

 

以前我第一次接触unity3d的时候也碰到过这个单机无法使用的问题,当时没解决。主要是不是一定要解决,那时属于个人研究性质的,反正用WebBrowser加载Flash Playerhtml文件时可以显示的,就到此为止了。这次是工作,必须解决,不然下一步没法进行。

看来“必须”很重要。

2016-08-29 19:53:37 tankerhunter 阅读数 3069
  • Unity3D入门到精通-(3)Unity资源管理精讲

    本次系列课程的目标是让Unity3D初学者掌握Unity3d的资源管理技术进行了全面介绍,特别对AssetBundle资源如何进行更新,以及加载(依赖资源加载)进行了系统的介绍。 适合对象:Unity初学开发者,Unity中级开发者,网络程序开发者,所有对游戏开发有兴趣的人员。 学习条件:有一定的Unity3D基础,了解C#的基本开发知识。

    4645 人正在学习 去看看 张刚

在Unity 的 安装目标(一般是”D:\Program Files\Unity\Editor\Data\Mono\lib\mono\2.0“)中有一些直接可以放置在Plugins文件夹下的动态连接库文件,其中System.Drawing和Sytem.Windows.Form是文本的核心。

由于需要一个展示用的Demo,其中需要修改一些颜色的参数,最好能有个对话框。刚好前一段时间用到了选择文件和保存文件的winform的文件对话框,这里就试了试利用winform自己的颜色调节对话框来选择一种颜色吧。在实际操作过程中遇到了颜色转换问题,加深了对颜色格式的认识。、

(如上图所示,由于不确定项目经理需要什么背景颜色,或者到时候又需要修改比较麻烦,这里就直接写个背景色修改,让他自己改去。)

点击背景色按扭(button 图片设置为透明了),希望弹出一个对话框:

在Start里添加这个Button的事件:(或者在面版上注册)

colorBtn.onClick.AddListener (SetBgColor);

我们需要的是在SetBgColor这个方法中,出现一个对话框,因此在Start中还要初始化一个全局的颜色对话框:(private ColorDialog dialog;

       dialog = new ColorDialog ();
        dialog.AllowFullOpen=true;
        dialog.FullOpen=false;
        dialog.ShowHelp=false;

在SetBgColor中,打开这个对话框,并等待用户选择结束:

 //        file://恢复原
//        dialog.ShowDialog();
        if (DialogResult.OK == dialog.ShowDialog()) {
            System.Drawing.Color m_color = dialog.Color;

            UnityEngine.Color newColor = W2UColor(m_color);
            m_Camera.backgroundColor =newColor;
        }

其中W2UColor是另一个方法,用于将Winform中的颜色转换为Unity3d中的颜色,这是下一步的问题,先看看打开的效果吧:(由于写在主线程中了,unity会卡死等待结束,如果需要卡死等待,更标准的应该要开一个线程才行)

打开到是好办,关键是unity 的rgba  和 System.Drawing的rgba 不一样,要进行一点换算,于是在W2UColor中写入如下内容便可:

UnityEngine.Color newColor = new UnityEngine.Color (color.R / 255f, color.G/ 255f, color.B/ 255f, color.A/255f);
        return newColor;

最后再来看看得到的背景效果,要不就选左下第二个试试:

貌似还可以,唯一的缺点就是winform界面有点丑了~

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

。。。。。。。。。。。。。。。。。。

经过一段时间的研究,其实还有更合理的一种方法,便是直接访问windows的Api打开对应的窗口,涉及到c#和window数据类型的转换,不过网上相关的资料也是比较多的

windos窗口操作API

ColorDialog

C#中对应win32 API原型函数的一些数据类型


最后实现可用的相关C#类如下:

  [DllImport("comdlg32.dll", CharSet = CharSet.Auto)]
    public static extern bool ChooseColorA([In, Out] CHOOSECOLOR pChoosecolor);//对应的win32API

public class CHOOSECOLOR
{
    public Int32 lStructSize = Marshal.SizeOf(typeof(CHOOSECOLOR));
    public IntPtr hwndOwner;
    public IntPtr hInstance;
    public Int32 rgbResult;
    public IntPtr lpCustColors;
    public Int32 Flags;
    public IntPtr lCustData = IntPtr.Zero;
    public WndProc lpfnHook;
    public string lpTemplateName;
}

在其他脚本里调用就可以弹出颜色选择框了,注意到其实Unity自身也有对16进制颜色转换的方法:

ColorUtility.TryParseHtmlString 可以直接将16进制转换为Color

ColorUtility.ToHtmlStringRGBA则相反

没有更多推荐了,返回首页