Embed Unity3D App Inside Wpf Application

Embed Unity3D app inside WPF application

This can be done but it's worth noting that it will only work on Windows.

It used to be hard to do this but Unity recently(4.5.5p1) added -parentHWND command that can be used to embed its program into another program. All you have to do is build your Unity app, then from WPF, start it with the Process API. You can then pass the -parentHWND parameter to the Unity app.

process.StartInfo.FileName = "YourUnityApp.exe";
process.StartInfo.Arguments = "-parentHWND " + panel1.Handle.ToInt32() + " " + Environment.CommandLine;

For commutation between the two, you can either use TCP or Named Pipes.

Below is a complete sample of the embed code from Unity's website. You can get the whole project here. Make sure to name the Unity's build exe file "UnityGame.exe" then place it in the-same directory as the WPF exe program.

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();
}
}
}

Embed Unity3D app inside WPF *without* having it take up the whole window

Use a WindowsFormsHost or HwndHost control in your WPF. The hwnd is in the host control's Handle property. So you can change this line to put Unity in just the host control.

process.StartInfo.Arguments = "-parentHWND " + hwndHost.Handle.ToInt32() + " " + Environment.CommandLine;

And remove the code that gets the hwnd for the floating frame

HwndSource source = (HwndSource)HwndSource.FromVisual(p);

Embed one Unity .exe file into multiple WPF Pages

I am the author of one of the answers on another related question on Stack Overflow. I made a repository which is a fork of your repository that has the behaviour as you intended.

I saw you like to work with Frames and that's okay, but because of that you had two unwanted behaviours I fixed for you in regard to the original repository.

  • The fact that the navigation panel was still showing up can be avoided with the attribute NavigationUIVisibility="Hidden" in the Frame of the MainWindow.xaml .

  • You also told me via e-mail that "the second time" a button was clicked, the WinformsHost was not showing up. This was because the changing the content of the Frame is not by definition triggering a refresh. You can force this refresh by the line Page2_UnityFrame.NavigationService.Refresh(); and similar for Page3 and Page4.

I tested with this code and now I think the behaviour is exactly as you wanted. Nevertheless, I would like to take the opportunity to give some extra advice regarding this version of the application.

  • I took the liberty to put your Unity-files (and related folders) and relocated them to a folder called All_Unity_Files . Than, in a post-build-event of the Container-project, I can specify that these Unity-files (and folders) need to be copied to the target directory. This is done via the command: xcopy "$(SolutionDir)..\All_Unity_Files" "$(TargetDir)" /s /y /r /d . So if you now download this code or clone the reposity, if the build succeeds, the required Unity files are automatically in the right directory to launch the application.
  • Second, I also renamed the Frames of the pages to Page1_UnityFrame , Page2_UnityFrame and so on. Before, these frames and the main frame in MainWindow.xaml all had the same x:Name (UnityFrame) and that is something that I would not recommend. If you than change in the code-behind, it is difficult to determine which frame you are getting or setting and as a consequence, more difficult to debug.
  • As a third and final remark, I would recommend to inform yourself about the possibility of data binding via MVVM. This will minimalize the amount of code in the code-behind and especially if your application will grow, it will be easier to maintain.

Unity3D with WPF application

My advice would be to Windows Communication Foundation [WCF]

Mono does support this:
http://www.mono-project.com/docs/web/wcf/

I haven't tried it however try these links:
https://www.youtube.com/watch?v=v4cXuiZyJQ8
https://www.youtube.com/watch?v=vROXfatVTJQ

Fallback would be NamedPipeServer/Client using my previous post that should hopefully work now [untested on Unity 5]

named pipe client won't work in unity3d

Combine a XAML (WPF) app in Unity with MixedRealityToolkit

There is a toolkit in the Unity Asset Store called NoesisGUI, which lets you define interfaces in XAML. It's not 100% compatible with UWP or WPF, but at least it would let you work in XAML and should be reasonably easy to adapt to. If you can't find a better way, this might be the least horrible alternative.



Related Topics



Leave a reply



Submit