WinForm内嵌Unity3D

来源:互联网 发布:淘宝 3d试衣 编辑:程序博客网 时间:2024/05/16 05:28

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


0 0
原创粉丝点击