桌面WPF程序嵌入Unity3D引擎(standalone)并实现通讯。

把Unity3D嵌入winform或者wpf程序,过去大部分使用UnityWebPlayer插件来实现,这个插件其实就是网页上播放unity页游的插件。

但是使用UnityWebPlayer嵌入桌面开发有各种问题,我认为最大的问题是效率问题(加载缓慢),毕竟是网页的加载方式,而且可以确认未来也不会得到任何优化。

由于WebGL的高速发展,unity公司认识到了webplayer十分鸡肋,毕竟WebGL不需要任何插件可以直接显示3d内容了,所以Unity3D在5.4.x版本以后明确表示,不再支持webplayer了,所以桌面上UnityWebPlayer插件能不用也就别用了吧。

将Unity嵌入桌面程序最好的方式是嵌入unity生成的exe程序,winform程序和unity之间通过socket进行通讯,我认为

这也是效率最高,效果最好和最好实现的方式。在Unity程序脚本中,嵌入socket内容,我推荐做成客户端(client),使用wpf程序做服务器端,这是一个谁是主体的问题。

这样wpf可以加载多个unity程序。

 


嵌入后的结果如下图所示(请无视具体内容):


下面简单写了一个脚本,其中man是游戏中的一个gameobject对象。

就是上图中穿蓝衣服的男人。在他身上挂着socket脚本如下

using UnityEngine; using System.Collections; using System.Net.Sockets; using System; public class demoshows : MonoBehaviour { public GameObject man; const int portNo = 500; private TcpClient _client; byte[] data; string Error_Message; void Start () { try { this._client = new TcpClient(); this._client.Connect("127.0.0.1", portNo);
            data = new byte[this._client.ReceiveBufferSize]; //SendMessage(txtNick.Text); SendMessage("Unity Demo Client is Ready!"); this._client.GetStream().BeginRead(data, 0, System.Convert.ToInt32(this._client.ReceiveBufferSize), ReceiveMessage, null);
        } catch (Exception ex)
        {


        }
       


    } void Update () {        
        transform.Rotate(new Vector3(0, 1, 0),0.1f);
    } public void rotation()

    {

        transform.Rotate(new Vector3(0, 10, 0)); //targetRotation = Quaternion.Euler(45.0f, 45.0f, 45.0f); //// 直接设置旋转角度  //transform.rotation = targetRotation; ////man.transform.rotation.SetAxisAngle(new Vector3(0, 1, 0), 30);;   } public void translateX(float x)

    {

        transform.Translate(new Vector3(x,0,0));
                                                                                                                      
    } public void translateY(float y)

    {

        transform.Translate(new Vector3(0, y, 0));
                                                                                                         
    } public void translateZ(float z)

    {

        transform.Translate(new Vector3(0, 0, z));
                                                                                                                   
    } void OnGUI()
    {
        GUI.Label(new Rect(50, 50, 150,50 ), Error_Message);
    } public new void SendMessage(string message)
    { try {
            NetworkStream ns = this._client.GetStream(); byte[] data = System.Text.Encoding.ASCII.GetBytes(message);
            ns.Write(data, 0, data.Length);
            ns.Flush();
        } catch (Exception ex)
        {
            Error_Message = ex.Message; //MessageBox.Show(ex.ToString());  }
    } public void ReceiveMessage(IAsyncResult ar)
    { try { //清空errormessage Error_Message = ""; int bytesRead;
            bytesRead = this._client.GetStream().EndRead(ar); if (bytesRead < 1)
            { return;
            } else {
                Debug.Log(System.Text.Encoding.ASCII.GetString(data, 0, bytesRead)); string message = System.Text.Encoding.ASCII.GetString(data, 0, bytesRead); switch (message)
                { case "1":
                        translateX(1); break; case "2":
                        translateX(-1); break; case "3":
                        translateY(1); break; case "4":
                        translateY(-1); break; case "5":
                        translateZ(1); break; case "6":
                        translateZ(-1); break; default:
                        Error_Message = "unknown command"; break;


                }




            } this._client.GetStream().BeginRead(data, 0, System.Convert.ToInt32(this._client.ReceiveBufferSize), ReceiveMessage, null);
        } catch (Exception ex)
        {
            Error_Message = ex.Message;
        }
    } void OnDestroy()
    { this._client.Close();
    }
}
脚本很简单,就是通过向unity程序发送消息(1~6)实现模型的平移。

服务器端,在wpf程序中简单建立一个socket类,ip和端口要和unity对应,在程序启动时先建立服务器端。


使用socket一定要注意使用线程,并且退出时及时结束

下面大概就是server的启动和释放,效果还好,至少不卡死

TcpServer WpfServer; int size_state = 0; public MainWindow()
        {
            InitializeComponent(); this.Closed += MainWindow_Closed; this.Activated += MainWindow_Activated; this.Deactivated += MainWindow_Deactivated;



            WpfServer = new TcpServer();
            WpfServer.StartServer();


        } void MainWindow_Closed(object sender, EventArgs e)
        {
            unityhost.Form1_FormClosed();


            WpfServer.QuitServer();

        }

如何嵌入?

 

首先在wpf程序中建立一个winform的自定义控件(不是wpf控件)usercontrol

在usercontrol内新建一个panel(或者其他带有句柄的控件),并设置dock属性为fill。

启动unity.exe,通过几个api将unity窗口附加在panel句柄上。

(说明:借鉴别人的程序)

需要把unity程序命名为child.exe,放在下面指定位置(Debug\UnityApp\Child.exe,或者release)

process.StartInfo.FileName =Application.StartupPath +@"\UnityApp\Child.exe";

详细代码如下:

using System; using System.Windows.Forms; using System.Runtime.InteropServices; using System.Diagnostics; using System.Threading; namespace Demo_Song
{ public partial class UnityControl : UserControl
    {
        [DllImport("User32.dll")] static extern bool MoveWindow(IntPtr handle, int x, int y, int width, int height, bool redraw); internal delegate int WindowEnumProc(IntPtr hwnd, IntPtr lparam);
        [DllImport("user32.dll")] internal static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc func, IntPtr lParam);

        [DllImport("user32.dll")] static extern int SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); private Process process; private IntPtr unityHWND = IntPtr.Zero; private const int WM_ACTIVATE = 0x0006; private readonly IntPtr WA_ACTIVE = new IntPtr(1); private readonly IntPtr WA_INACTIVE = new IntPtr(0); public UnityControl()
        {
            InitializeComponent(); this.Load += UnityControl_Load;
            panel1.Resize+=panel1_Resize;
        } private void UnityControl_Load(object sender, EventArgs e)
        { try {
                process = new Process();
                process.StartInfo.FileName =Application.StartupPath +@"\UnityApp\Child.exe";
                process.StartInfo.Arguments = "-parentHWND " + panel1.Handle.ToInt32() + " " + Environment.CommandLine;
                process.StartInfo.UseShellExecute = true;
                process.StartInfo.CreateNoWindow = true;

                process.Start();

                process.WaitForInputIdle(); // Doesn't work for some reason ?! //unityHWND = process.MainWindowHandle;  EnumChildWindows(panel1.Handle, WindowEnum, IntPtr.Zero);

                unityHWNDLabel.Text = "Unity HWND: 0x" + unityHWND.ToString("X8");
            } catch (Exception ex)
            {
                unityHWNDLabel.Text = ex.Message; //MessageBox.Show(ex.Message);  }
        } internal void ActivateUnityWindow()
        {
            SendMessage(unityHWND, WM_ACTIVATE, WA_ACTIVE, IntPtr.Zero);
        } internal void DeactivateUnityWindow()
        {
            SendMessage(unityHWND, WM_ACTIVATE, WA_INACTIVE, IntPtr.Zero);
        } private int WindowEnum(IntPtr hwnd, IntPtr lparam)
        {
            unityHWND = hwnd;
            ActivateUnityWindow(); return 0;
        } private void panel1_Resize(object sender, EventArgs e)
        {
            MoveWindow(unityHWND, 0, 0, panel1.Width, panel1.Height, true);
            ActivateUnityWindow();
        } // Close Unity application internal void Form1_FormClosed()
        { try {
                process.CloseMainWindow();

                Thread.Sleep(1000); while (process.HasExited == false)
                    process.Kill();
            } catch (Exception)
            {

            }
        } internal void Form1_Activated()
        {
            ActivateUnityWindow();
        } internal void Form1_Deactivate()
        {
            DeactivateUnityWindow();
        }
    }  }




  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
WPF(Windows Presentation Foundation)是微软公司推出的一种用户界面开发框架,它可以用于创建丰富的、直观的Windows应用程序Unity3D是一种跨平台的游戏引擎,广泛应用于游戏开发、VR/AR应用程序等。将Unity3D嵌入WPF应用程序中可以实现更丰富的用户体验和交互。 在将Unity3D嵌入WPF应用程序中时,首先需要在WPF项目中添加Unity3DUnityPlayer控件。然后,可以通过调用Unity3D的接口方法来加载和显示Unity3D场景,并在WPF应用程序中设置对象之间的交互。通过这种方式,可以在WPF应用程序中直接运行Unity3D场景,使用户可以更直观地与虚拟世界进行交互。 嵌入Unity3DWPF应用程序中的好处是可以将游戏和应用程序的功能结合在一起,为用户提供更多更丰富的功能和体验。例如,可以在WPF应用程序中创建一个交互式的虚拟展示环境,用户可以在展示环境中与虚拟物体进行交互、操作。另外,嵌入Unity3D还可以增加应用程序的趣味性和视觉效果,使应用程序更加吸引人。 然而,嵌入Unity3DWPF应用程序中也存在一些挑战和限制。由于WPFUnity3D是两种不同的技术平台,需要做好平台和版本之间的兼容性工作。同时,开发人员还需要熟悉和掌握Unity3D的开发技术,以便在WPF应用程序中正确使用和操作Unity3D场景。 综上所述,将Unity3D嵌入WPF应用程序中可以提供更丰富的用户体验和功能,但也需要在技术上做好兼容性工作,并投入额外的开发资源和学习成本。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值