C# 在WPF应用程序中嵌入Unity3D应用程序

C# 在WPF应用程序中嵌入Unity3D应用程序,c#,wpf,unity3d,3d,cad,C#,Wpf,Unity3d,3d,Cad,我想在WPF中开发一个新的CAD软件,而不是使用WPF 3D,是否可以使用Unity3D作为我的图形引擎,能够根据WPF中的数据对象旋转、平移、缩放和查看3D图形对象 我问这个问题的原因是,Unity是一个游戏引擎,它使用C#作为脚本,但它不提供任何来自WPF应用程序的集成(将Unity嵌入WPF) 我在unity论坛上问了这个问题,没有找到任何好的答案,所以向更多的观众提问。这是可以做到的,但值得注意的是,它只在Windows上工作 以前很难做到这一点,但Unity最近(4.5.5p1)添加了

我想在WPF中开发一个新的CAD软件,而不是使用WPF 3D,是否可以使用Unity3D作为我的图形引擎,能够根据WPF中的数据对象旋转、平移、缩放和查看3D图形对象

我问这个问题的原因是,Unity是一个游戏引擎,它使用C#作为脚本,但它不提供任何来自WPF应用程序的集成(将Unity嵌入WPF)


我在unity论坛上问了这个问题,没有找到任何好的答案,所以向更多的观众提问。

这是可以做到的,但值得注意的是,它只在Windows上工作

以前很难做到这一点,但Unity最近(4.5.5p1)添加了一个命令,可用于将其程序嵌入到另一个程序中。你所要做的就是构建你的Unity应用程序,然后从WPF开始,用
进程
API启动它。然后可以将参数传递给Unity应用程序

process.StartInfo.FileName = "YourUnityApp.exe";
process.StartInfo.Arguments = "-parentHWND " + panel1.Handle.ToInt32() + " " + Environment.CommandLine;
对于两者之间的交换,可以使用TCP或

下面是Unity网站上嵌入代码的完整示例。你可以得到整个项目。确保将Unity的build exe文件命名为“UnityGame.exe”,然后将其放置在与WPF exe程序相同的目录中

namespace Container
{
    public partial class Form1 : Form
    {
        [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 Form1()
        {
            InitializeComponent();

            try
            {
                process = new Process();
                process.StartInfo.FileName = "UnityGame.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)
            {
                MessageBox.Show(ex.Message + ".\nCheck if Container.exe is placed next to UnityGame.exe.");
            }

        }

        private void ActivateUnityWindow()
        {
            SendMessage(unityHWND, WM_ACTIVATE, WA_ACTIVE, IntPtr.Zero);
        }

        private 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
        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            try
            {
                process.CloseMainWindow();

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

            }
        }

        private void Form1_Activated(object sender, EventArgs e)
        {
            ActivateUnityWindow();
        }

        private void Form1_Deactivate(object sender, EventArgs e)
        {
            DeactivateUnityWindow();
        }
    }
}

这是可以做到的,但值得注意的是,它只能在Windows上工作

以前很难做到这一点,但Unity最近(4.5.5p1)添加了一个命令,可用于将其程序嵌入到另一个程序中。你所要做的就是构建你的Unity应用程序,然后从WPF开始,用
进程
API启动它。然后可以将参数传递给Unity应用程序

process.StartInfo.FileName = "YourUnityApp.exe";
process.StartInfo.Arguments = "-parentHWND " + panel1.Handle.ToInt32() + " " + Environment.CommandLine;
对于两者之间的交换,可以使用TCP或

下面是Unity网站上嵌入代码的完整示例。你可以得到整个项目。确保将Unity的build exe文件命名为“UnityGame.exe”,然后将其放置在与WPF exe程序相同的目录中

namespace Container
{
    public partial class Form1 : Form
    {
        [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 Form1()
        {
            InitializeComponent();

            try
            {
                process = new Process();
                process.StartInfo.FileName = "UnityGame.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)
            {
                MessageBox.Show(ex.Message + ".\nCheck if Container.exe is placed next to UnityGame.exe.");
            }

        }

        private void ActivateUnityWindow()
        {
            SendMessage(unityHWND, WM_ACTIVATE, WA_ACTIVE, IntPtr.Zero);
        }

        private 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
        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            try
            {
                process.CloseMainWindow();

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

            }
        }

        private void Form1_Activated(object sender, EventArgs e)
        {
            ActivateUnityWindow();
        }

        private void Form1_Deactivate(object sender, EventArgs e)
        {
            DeactivateUnityWindow();
        }
    }
}

是的,这可能与团结有关。说真的,你可以用任何3D游戏引擎进行平移、旋转和缩放,所以我说不出这个问题的重点。非常感谢。我修改了我的问题,请看一看。您是否曾经尝试过使用WPF作为主应用程序并调用Unity3D来显示图形显示?您可以使用TCP或两者之间的通信,并在文章中介绍了如何将Unity应用程序嵌入WPF。我会先检查一下。它是为CAD应用程序设计的WPF控件。Unity3D并不是完全免费的,更多信息:是的,它可以使用Unity。说真的,你可以用任何3D游戏引擎进行平移、旋转和缩放,所以我说不出这个问题的重点。非常感谢。我修改了我的问题,请看一看。您是否曾经尝试过使用WPF作为主应用程序并调用Unity3D来显示图形显示?您可以使用TCP或两者之间的通信,并在文章中介绍了如何将Unity应用程序嵌入WPF。我会先检查一下。这是一个为CAD应用程序设计的WPF控件。Unity3D并非完全免费,更多信息:感谢此解决方案。看起来这对您提供的示例有效,但对其他示例无效。有什么问题吗?可能是unity项目的加载时间吗?我注意到unity项目已经加载,但它没有显示在表单中。ThanksI解决了添加process.StartInfo.WindowStyle=ProcessWindowStyle.Maximized;太棒了!你的回答拯救了数千人的生命!非常有用非常感谢你。但这是针对Windows窗体项目的。要使其在WPF中工作,请使用xmlns:wf=“clr namespace:System.Windows.Forms;assembly=System.Windows.Forms”c#几乎相同。在窗口中执行“attachUnity();”,而不是加载构造函数,因为Hwnd还没有准备好。然后它工作了,但是如果我离开窗口并返回,键盘输入将被禁用。出于某种原因,ActivateUnityWindow();工作不正常。我试着让它在激活前进入睡眠状态,但没有帮助。相反,我创建了一个按钮,并将ActivateUnityWindow()放入其中;在click事件中。这是有原因的。为了实现自动化,我添加了var-peer=new-ButtonAutomationPeer(btn_测试);IInvokeProvider invokeProvider=peer.GetPattern(PatternInterface.Invoke)作为IInvokeProvider;invokeProv.Invoke();要模拟按钮,请单击。您可以隐藏按钮,它仍然可以工作感谢此解决方案。看起来这对您提供的示例有效,但对其他示例无效。有什么问题吗?可能是unity项目的加载时间吗?我注意到unity项目已经加载,但它没有显示在表单中。ThanksI解决了添加process.StartInfo.WindowStyle=ProcessWindowStyle.Maximized;太棒了!你的回答拯救了数千人的生命!非常有用非常感谢你。但这是针对Windows窗体项目的。要使其在WPF中工作,请使用xmlns:wf=“clr namespace:System.Windows.Forms;assembly=System.Windows.Forms”c#几乎相同。在窗口中执行“attachUnity();”,而不是加载构造函数,因为Hwnd还没有准备好。然后它工作了,但是如果我离开窗口并返回,键盘输入将被禁用。出于某种原因,ActivateUnityWindow();工作不正常。我试着让它在激活前进入睡眠状态,但没有帮助。相反,我创建了一个按钮,并将ActivateUnityWindow()放入其中;在click事件中。这是有原因的。为了实现自动化,我添加了var-peer=new-ButtonAutomationPeer(btn_测试);IInvokeProvider invokeProvider=peer.GetPattern(PatternInterface.Invoke)作为IInvokeProvider;invokeProv.Invoke();要模拟按钮,请单击。你可以隐藏按钮,它仍然可以工作