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模式下編譯
轉載請註明出處
請耐心讀完