zoukankan      html  css  js  c++  java
  • WinForm内嵌Unity3D

    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


  • 相关阅读:
    5.搜索-dfs、回溯、bfs
    4.排序算法
    3.二分查找
    2.双指针
    1.贪心算法
    【目录】leetcode刷题
    深度学习的优化与正则化
    什么是深度学习
    循环神经网络
    Failed to execute 'index' on 'IDBObjectStore': The specified index was not found.
  • 原文地址:https://www.cnblogs.com/sparkleDai/p/7604945.html
Copyright © 2011-2022 走看看