zoukankan      html  css  js  c++  java
  • WPF内嵌Unity并实现双向通讯

    引自:http://www.unitymanual.com/blog-11491-1628.html

    博客原文:

    说明

    需求是这样的 —— 一个编辑器。既能够方便得编辑各种静态数据表(Excel),又能够对表中指定的资源进行预览(Spine骨骼动画)。

    问题在于适合做表编辑器的软件框架,如WPF、Winform等等,都没有相应的Spine渲染库;而支持Spine渲染的框架,如unity3d、MonoGame、Cocos2D等,又或存在Excel库不好用或者缺少软件向控件的问题。

    我们采取的方案是,使用WPF做它擅长的软件功能,而使用Unity作为“渲染控件”。

    技术路线

    为什么不用WPF嵌入unitywebplayer?
    将Unity嵌入WPF(或Winform),比较常见的做法是通过ActiveX将UnityWebPlayer包装成一个真正的控件。但由于UnityWebPlayer运行在安全沙箱中,它至少有以下两个问题:

    • 没有System.IO的程序集,只能够加载assetbundle,无法加载未经管线预处理的Spine
    • 没法加载Excel文件
    • 如何将Unity Windows Standalone 嵌入WPF
      将Standalone作为一个控件,指定到WPF父窗口下。采用WindowsAPI来实现。

    如何实现Standalone与WPF双向通信
    需要能在WPF中设定Standalone的当前Spine,也要能在Standalone上操作由WPF维护的Excel。一个可行的方案是pipes。
    这篇讨论中说Unity不初始化NamedPipelineServer。那么就拿它当NamedPipelineClient好了。

    将Unity Windows Standalone 嵌入WPF

    核心代码如下:
    // 定义一个WPF用户控件,来定义Standalone的尺寸
    ProcessStartInfo info = new ProcessStartInfo(“UnityControl.exe”);
    info.UseShellExecute = true;
    info.WindowStyle = ProcessWindowStyle.Minimized;
    m_AppProcess = System.Diagnostics.Process.Start(info);
    m_AppProcess.WaitForInputIdle();
    this.Dispatcher.BeginInvoke(
    System.Windows.Threading.DispatcherPriority.ApplicationIdle,
    appIdleEvent, this, null);

    // Application Idle 事件处理
    WindowInteropHelper helper = new WindowInteropHelper(Window.GetWindow(this));
    IntPtr ptr = helper.Handle;
    SetParent(app.MainWindowHandle, helper.Handle);
    SetWindowLong(new HandleRef(this, app.MainWindowHandle), GWL_STYLE, WS_VISIBLE);
    MoveWindow(app.MainWindowHandle, (int)control.Margin.Left, (int)control.Margin.Top, (int)control.Width, (int)control.Height, true);
    

    实现Unity Windows Standalone与WPF通讯

    // WPF
    NamedPipeServerStream pipeServer = new NamedPipeServerStream(
        "testpipe", 
        PipeDirection.InOut, 
        1);
    pipeServer.WaitForConnection();
    
    string str = "hello from server!";
    byte[] outBuffer = Encoding.Unicode.GetBytes(str);
    int len = outBuffer.Length;
    pipeServer.WriteByte((byte)(len / 256));
    pipeServer.WriteByte((byte)(len % 256));
    pipeServer.Write(outBuffer, 0, len);
    pipeServer.Flush();
    
    len = pipeServer.ReadByte() * 256;
    len += pipeServer.ReadByte();
    byte[] inBuffer = new byte[len];
    pipeServer.Read(inBuffer, 0, len);
    string remoteInfo = Encoding.Unicode.GetString(inBuffer);
    
    lbRemoteInfo.Content = remoteInfo;
    
    // Unity 
    pipeClient = new NamedPipeClientStream(
        ".", "testpipe", 
        PipeDirection.InOut);
    pipeClient.Connect(10000);
    
    int len = pipeClient.ReadByte() * 256;
    len += pipeClient.ReadByte();
    byte[] inBuffer = new byte[len];
    pipeClient.Read(inBuffer, 0, len);
    remoteInfo = Encoding.Unicode.GetString(inBuffer);
    
    pipeClient.Flush();
    string str = "hello from client!";
    byte[] outBuffer = Encoding.Unicode.GetBytes(str);
    len = outBuffer.Length;
    pipeClient.WriteByte((byte)(len / 256));
    pipeClient.WriteByte((byte)(len % 256));
    pipeClient.Write(outBuffer, 0, len);
    pipeClient.Flush();
    

    效果

    做了一个简单的测试,看上去效果不错:

    • Unity(Client)被嵌入WPF(Server)窗口
    • WPF向Unity发送一条”hello from server!”,在Unity控件的左上角显示
    • Unity向WPF发送一条”hello from client!”,显示在一个WPF的Label上

    说明和参考

    MSDN管道通讯Demo
    如何:使用命名管道进行网络进程间通信

    Mono不支持管道安全级别
    不用就可以了,NamedPipeServerStream构造函数里,不要填TokenImpersonationLevel
    StackOverflow

    Unity无法启动NamedPipelineServer
    StackOverflow

    在Unity中启用System.IO.Pipes命名空间
    在Player Settings中,将.net版本设置为.net2.0,默认是.net2.0 subset
    StackOverflow

    如何将窗体嵌入为控件
    C#自定义控件:WinForm将其它应用程序窗体嵌入自己内部

  • 相关阅读:
    NSPredicate
    label 下划线加自动换行
    【搬运】快速增加文档注释
    NSSortDescriptor 数组排序
    【搬运】打开模拟器沙盒目录
    NSTimer 详解
    Android打开外部DB文件
    图片压缩与缓存
    StartService与BindService
    Android发送通知栏通知
  • 原文地址:https://www.cnblogs.com/nearpengju123/p/4494573.html
Copyright © 2011-2022 走看看