您的位置:首页 > 移动开发 > Unity3D

WinForm内嵌Unity3D

2015-11-17 17:55 706 查看
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: