1. 程式人生 > >WinForm內嵌Unity3D 的所有程式碼包括Winform程式碼、unity程式碼、類庫

WinForm內嵌Unity3D 的所有程式碼包括Winform程式碼、unity程式碼、類庫

轉載自:          https://blog.csdn.net/xxdddail/article/details/49890643

程式碼下載:      http://download.csdn.net/detail/xxdddail/9277447


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模式下編譯


轉載請註明出處

請耐心讀完