Unity全新UI系统"dear imgui"

前言

  • 有没有人跟我有过同样的想法,觉得Unityimgui相当繁琐,每次想写点编辑器扩展都得自己写一大堆的东西,本来就一直在网上找找有没有一些造好的轮子可以使用,可惜没有找到。后面在找到dear imgui的时候,就在想这么牛逼的东西,能集成到unity里面就好了啊,然后还真找到了解决方案,使用unity的原生渲染接口渲染dear imgui就可以了。
  • 有趣的是,因为工作上杂事比较多,这个集成技术测试,却不得不先放一放,这里只是临时作个记录,以方便后续还可以接着研究。所以这里其实是一个未完成的解决方案。
  • 我目前只以我现有的环境做记录,暂不考虑兼容任何其他的平台或者抽象API,我的环境是win10 64位系统 directx11 visual studio 2017 unity 2018.3.f2

参考链接

  • dear imgui:https://github.com/ocornut/imgui
  • unity 原生渲染接口:Unity Low-level native plug-in interface

功能实现

  1. 新建c++动态库工程命名RenderingPlugin,在unity安装目录下Unity2018.3.0f2\Editor\Data\PluginAPI找到接口文件,添加到工程中
  2. RenderingPlugin按着Unity Low-level native plug-in interface教程添加代码
  3. 再将dear imgui的工程文件添加到RenderingPlugin工程文件中,同时添加dear imguiimgui_impl_win32&imgui_impl_dx11,这是我们目前所需要的平台代码
  4. 然后再添加如下代码(windows编程真心不熟,全靠乱蒙),
//接收windwos的事件 传给imgui
extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	if (ImGui_ImplWin32_WndProcHandler(hwnd, msg, wParam, lParam))
	{
		return true;
	}
	//调用原生的事件
	return CallWindowProcW(_oldWndProc,hWnd,msg, wParam,lParam);
}

//为c#添加设置窗口句柄的接口函数,同时设置imgui的初始化
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API
SetHwnd(HWND hwnd)
{
	_hwnd = hwnd;
	
	_oldWndProc=(WNDPROC)SetWindowLongPtr(_hwnd, GWLP_WNDPROC,(LONG_PTR)WndProc);
	
	// TODO: Initialization code.
	auto* D3D = s_UnityInterfaces->Get<IUnityGraphicsD3D11>();
	D3D11Device = D3D->GetDevice();
	D3D11Device->GetImmediateContext(&D3D11DeviceContext);
	ImGui::CreateContext();
	ImGuiIO& io = ImGui::GetIO(); (void)io;
	ImGui_ImplWin32_Init(_hwnd);
	ImGui_ImplDX11_Init(D3D11Device, D3D11DeviceContext);
}
//...
//在结束的时候,清理imgui
static void UNITY_INTERFACE_API OnGraphicsDeviceEvent(UnityGfxDeviceEventType eventType)
{
	// Create graphics API implementation upon initialization
	if (eventType == kUnityGfxDeviceEventInitialize)
	{
		assert(s_CurrentAPI == NULL);
		s_DeviceType = s_Graphics->GetRenderer();
		s_CurrentAPI = CreateRenderAPI(s_DeviceType);
	}
	// Let the implementation process the device related events
	if (s_CurrentAPI)
	{
		s_CurrentAPI->ProcessDeviceEvent(eventType, s_UnityInterfaces);
	}

	// Cleanup graphics API implementation upon shutdown
	if (eventType == kUnityGfxDeviceEventShutdown)
	{
		delete s_CurrentAPI;
		s_CurrentAPI = NULL;
		s_DeviceType = kUnityGfxRendererNull;

		//TODO: user shutdown code
		ImGui_ImplDX11_Shutdown();
		ImGui_ImplWin32_Shutdown();
		D3D11DeviceContext->Release();
		ImGui::DestroyContext();
	}
}
//添加imgui 的demo作为测试
static void UNITY_INTERFACE_API OnRenderEvent(int eventID)
{
	ImGui_ImplDX11_NewFrame();
	ImGui_ImplWin32_NewFrame();
	ImGui::NewFrame();

	ImGui::ShowDemoWindow();

	ImGui::Begin("Hello, World!");
	ImGui::End();


	// 2. Show a simple window that we create ourselves. We use a Begin/End pair to created a named window.
	{
		static float f = 0.0f;
		static int counter = 0;

		ImGui::Begin("Hello, world!");                          // Create a window called "Hello, world!" and append into it.

		ImGui::Text("This is some useful text.");               // Display some text (you can use a format strings too)
		ImGui::Checkbox("Demo Window", &show_demo_window);      // Edit bools storing our window open/close state
		ImGui::Checkbox("Another Window", &show_another_window);

		ImGui::SliderFloat("float", &f, 0.0f, 1.0f);            // Edit 1 float using a slider from 0.0f to 1.0f
		ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color

		if (ImGui::Button("Button"))                            // Buttons return true when clicked (most widgets return true when edited/activated)
			counter++;
		ImGui::SameLine();
		ImGui::Text("counter = %d", counter);

		ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
		ImGui::End();
	}

	// 3. Show another simple window.
	if (show_another_window)
	{
		ImGui::Begin("Another Window", &show_another_window);   // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)
		ImGui::Text("Hello from another window!");
		if (ImGui::Button("Close Me"))
			show_another_window = false;
		ImGui::End();
	}

	ImGui::Render();
	ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
}
  1. RenderingPlugin生成的dll放在unityPlugin文件夹下,然后在脚本中添加unity的接口
	[DllImport("RenderingPlugin")]
	private static extern void SetHwnd(IntPtr hwnd);
	IEnumerator Start()
	{
		IntPtr hwnd = HWND.GetHwnd();
		SetHwnd(hwnd);
		yield return StartCoroutine("CallPluginAtEndOfFrames");
	}
	//在主线程中调用渲染接口
	private IEnumerator CallPluginAtEndOfFrames()
	{
		while (true) {
			// Wait until all frame rendering is done
			yield return new WaitForEndOfFrame();
			
			// Issue a plugin event with arbitrary integer identifier.
			// The plugin can distinguish between different
			// things it needs to do based on this ID.
			// For our simple plugin, it does not matter which ID we pass here.
			GL.IssuePluginEvent(GetRenderEventFunc(), 1);
		}
	}
  1. 运行截图
    Unity全新UI系统
    Unity全新UI系统
    Unity全新UI系统

待扩展

  • imgui的脚本扩展,可以支持c#pythonlua等多种选择
  • imgui有一个docking分支,用来写编辑器布局,是非常方便又灵活的
  • 最开始就是考虑为扩展unity编辑器而集成imgui的,但是因为unityEditorWindow的句柄太不可控了,问题太多,所以先测试了集成在UnityEditor.GameView,和发布exe测试,虽然上面有集成在EditorWindow里的截图,但这里只是一个初步的测试
  • 然而就算只为在UnityEditor.GameView与发布的exe集成了imgui也是有一定问题的,后续改进吧
  • 至于源码,由于是测试代码,凌乱不堪,暂时不考虑放出
  • 未完待续。。。

你可能感兴趣的:(Unity,C++,Unity,UGUI,Unity,Editor)