WinForm内嵌Unity3D

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/71880.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

用chatGPT从左右眼图片生成点云数据

左右眼图片 需求 需要将左右眼图像利用视差生成三维点云数据 先问问chatGPT相关知识 进一步问有没有现成的软件 chatGPT提到了OpenCV&#xff0c;我们让chatGPT用OpenCV写一个程序来做这个事情 当然&#xff0c;代码里面会有一些错误&#xff0c;chatGPT写的代码并不会做模…

并发编程--------JUC集合

并发集合 一、ConcurrentHashMap 1.1 存储结构 ConcurrentHashMap是线程安全的HashMap ConcurrentHashMap在JDK1.8中是以CASsynchronized实现的线程安全 CAS&#xff1a;在没有hash冲突时&#xff08;Node要放在数组上时&#xff09; synchronized&#xff1a;在出现hash…

案例12 Spring MVC入门案例

网页输入http://localhost:8080/hello&#xff0c;浏览器展示“Hello Spring MVC”。 1. 创建项目 选择Maven快速构建web项目&#xff0c;项目名称为case12-springmvc01。 2.配置Maven依赖 <?xml version"1.0" encoding"UTF-8"?><project xm…

Nacos AP架构集群搭建(Windows)

手写SpringCloud项目地址&#xff0c;求个star github:https://github.com/huangjianguo2000/spring-cloud-lightweight gitee:https://gitee.com/huangjianguo2000/spring-cloud-lightweigh 目录&#xff1a; 一&#xff1a;初始化MySQL 二&#xff1a;复制粘贴三份Nacos文…

vue2 封装 webSocket 开箱即用

第一步&#xff1a; 下载 webSocket npm install vue-native-websocket --save 第二步&#xff1a; 需要在 main.js 中 引入 import websocket from vue-native-websocket; Vue.use(websocket, , {connectManually: true, // 手动连接format: json, // json格式reconnection:…

【网络编程】万字详解||一个简单TCP服务器(TCP、线程池、守护进程)源码+介绍

TCP服务器 锁&#xff1a;Lock.hpp代码介绍 守护进程&#xff1a;daemonize.hpp代码说明 日志文件&#xff1a;log.hpp代码说明 任务处理 Task.hpp代码说明 线程池 ThreadPool.hpp代码说明 客户端 TCPClient.cc代码说明 服务器 TCPServer.cc代码说明 头文件包 util.hpp代码 Mak…

从源码层面深度剖析Spring循环依赖 | 京东云技术团队

以下举例皆针对单例模式讨论 图解参考 https://www.processon.com/view/link/60e3b0ae0e3e74200e2478ce 1、Spring 如何创建Bean&#xff1f; 对于单例Bean来说&#xff0c;在Spring容器整个生命周期内&#xff0c;有且只有一个对象。 Spring 在创建 Bean 过程中&#xff0…

Python Opencv实践 - 图像平移

import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/pomeranian.png", cv.IMREAD_COLOR)#图像平移 #cv.warpAffine(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]]) # M是仿射变换矩阵&#xff0c;对于平移来说M是一…

如何撰写一份清晰有效的说明文档

如何撰写一份清晰有效的说明文档 文章目录 导语1.明确读者群体&#xff1a;2.明确文档目的&#xff1a;3.提供清晰的结构&#xff1a;4.使用简洁明了的语言&#xff1a;5.提供具体的示例&#xff1a;6.注意文档格式和风格&#xff1a;7.接受反馈并更新文档&#xff1a;结语 导语…

如何使用Markdown编辑器?详细做法

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

原生JS手写扫雷小游戏

场景 实现一个完整的扫雷游戏需要一些复杂的逻辑和界面交互。我将为你提供一个简化版的扫雷游戏示例&#xff0c;帮助你入门。请注意&#xff0c;这只是一个基本示例&#xff0c;你可以根据自己的需求进行扩展和改进。 思路 创建游戏板&#xff08;Grid&#xff09;&#xff1…

网络基础——网络的由来与发展史

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 作者会持续更新网络知识和python基础知识&#xff0c;期待你的关注 目录 一、网络的由来 二、计算机网络的发展史 1、第一阶段 2、第二阶段 3、第三阶段 前言 每天都是使用网络&#xff0c;那么你知道网络…

StringBuilder的基本操作

1、为什么要学习StringBuilder? 1.1、String拼接100万次 String对象做字符串拼接&#xff0c;字符串直接拼接100万次&#xff0c;运行速度非常非常的慢&#xff0c;当数据量比较大的时候&#xff0c;一般不用字符串直接拼接 package stringdemo;public class StringTest {publ…

Spring Boot 项目应用消息服务器RabbitMQ(简单介绍)

一、背景 本章讲述的是在用户下单环节&#xff0c;消息服务器RabbitMQ 的应用 1.1 消息服务器的应用 在写一个电商项目的小demo&#xff0c;在电商项目中&#xff0c;消息服务器的应用&#xff1a; 1、订单状态通知&#xff1a;当用户下单、支付成功、订单发货、订单完成等…

SpringBoot3集成Quartz

标签&#xff1a;Quartz.Job.Scheduler&#xff1b; 一、简介 Quartz由Java编写的功能丰富的开源作业调度框架&#xff0c;可以集成到几乎任何Java应用程序中&#xff0c;并且能够创建多个作业调度&#xff1b; 在实际的业务中&#xff0c;有很多场景依赖定时任务&#xff0c…

谈谈什么是云计算?以及它的应用

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 作者会持续更新网络知识和python基础知识&#xff0c;期待你的关注 目录 ​编辑 一、什么是云计算 二、云计算的优势与劣势&#xff1f; 1、云计算的优势 ①提高资源利用率 ②提升效率 ③降低成本 2、云…

opencv带GStreamer之Windows编译

目录 1、下载GStreamer和安装2. GSTReamer CMake配置3. 验证是否配置成功 1、下载GStreamer和安装 下载地址如下&#xff1a; gstreamer-1.0-msvc-x86_64-1.18.2.msi gstreamer-1.0-devel-msvc-x86_64-1.18.2.msi 安装目录无要求&#xff0c;主要是安装完设置环境变量 xxx\1…

【java面向对象中static关键字】

提纲 static修饰成员变量static修饰成员变量的应用场景static修饰成员方法static修饰成员方法的应用场景static的注意事项static的应用知识&#xff1a;代码块static的应用知识&#xff1a;单例设计模式 static静态的意思&#xff0c;可以修饰成员变量&#xff0c;成员方法&a…

React 组件防止冒泡方法

背景 在使用 antd 组件库开发时&#xff0c;发现点击一个子组件&#xff0c;却触发了父组件的点击事件&#xff0c;比如&#xff0c;我在一个折叠面板里面放入一个下拉框或者对下拉框列表渲染做定制&#xff0c;每个下拉框候选项都有一个子组件… 解决 其实这就是 Javascri…

C++笔记之Eigen库的使用

C笔记之Eigen库的使用 code review! 文章目录 C笔记之Eigen库的使用0.矩阵构造和矩阵初始化1.声明一个2\*3的float矩阵&#xff1a;Matrix<float, 2, 3> matrix_23;2.初始化Matrix<float, 2, 3> matrix_23;- 使用逗号初始化器&#xff1a;- 使用赋值运算符逐个赋…
最新文章