大家好!我们是一对夫妻,正在制作一个名为Petunia's Purgatory的桌面伴侣游戏,游戏中你会经营一个可爱又神秘的农场,试图不被疯狂所困扰。
当我们决定制作一个不占据整个屏幕的桌面伴侣游戏时,我们进行了大量的google搜索来了解如何实现这一点。
结果证明,这并不是非常复杂的,但确实有很多陷阱。我想分享我们所学到的知识,以便其他人也能制作类似的游戏。
好吧,开始吧!请注意,这是一个相当技术性的内容,所以你需要了解C#的基本知识。虽然你不需要了解Windows编程(我也不了解!),但你需要有一定的编程基础。
-----------------------------------
设置
版本信息:本文使用的是Unity 6.2 for Windows。我无法保证其他版本的兼容性,且不支持Mac或Linux。
概念:一个桌面伴侣游戏实际上就是一个正常的Windows应用,但它没有边框。它的透明区域允许鼠标点击通过,因此它可以在你的桌面上轻松地与其他应用共存。
项目设置:
- 添加一个摄像机,并设置以下内容:
- 在环境选项卡中,设置背景类型为固体颜色,并将颜色设置为纯黑色(0 Alpha)
- 取消勾选后处理并确保抗锯齿关闭
- 某些原因,后处理不适合这种情况
- 添加一个UI事件系统(GameObject - UI - EventSystem)
- 在项目设置中,前往Player - Resolution and Presentation,并设置以下内容:
- 在后台运行:True
- 全屏模式:全屏窗口
- 可调整大小的窗口:False
- 在后台可见:True
- 允许全屏切换:False
----------------------------------------
代码
概念:我们将使用一些Windows函数来控制游戏窗口的呈现。事实上,我不知道这些函数内部的工作原理,但它们确实有效!
步骤 #1:基本设置
创建一个新MonoBehavior脚本(我称之为“TransparentAppController”),并将其附加到一个游戏对象(例如你的摄像机)
步骤 #2:Windows函数
添加以下行:
using System.Runtime.InteropServices;
声明以下变量,确保这些变量的名称和类型完全相同:
[DllImport("user32.dll")]
private static extern IntPtr GetActiveWindow();
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
const int GWL_EXSTYLE = -20;
const uint WS_EX_LAYERED = 0x00080000;
const uint WS_EX_TRANSPARENT = 0x00000020;
static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
private IntPtr hWnd;
步骤 #3:Unity逻辑
添加以下函数(这将捕获游戏获得焦点时的窗口ID)
private void OnApplicationFocus(bool hasFocus)
{
if (hasFocus)
{
hWnd = GetActiveWindow();
}
}
添加以下代码块到你的Update()函数
PointerEventData pointerEventData = new PointerEventData(EventSystem.current);
pointerEventData.position = Input.mousePosition;
List<RaycastResult> raycastResultList = new List<RaycastResult>();
EventSystem.current.RaycastAll(pointerEventData, raycastResultList);
bool isOverUI = raycastResultList.Count > 0;
if(isOverUI)
{
SetWindowLong(hWnd, GWL_EXSTYLE, WS_EX_LAYERED);
}
else
{
SetWindowLong(hWnd, GWL_EXSTYLE, WS_EX_LAYERED | WS_EX_TRANSPARENT);
}
如果你感兴趣的话,这段代码做了以下几件事情:
- 移除了游戏的边框,并使任何未渲染的区域透明
- 检查鼠标指针是否在可点击区域上,如果是,则允许游戏被点击。这防止了游戏在空白区域上阻塞桌面输入
步骤 #4(可选):始终置顶
这是一种可选的设置,如果你想让游戏始终置顶其他窗口,可以通过添加以下代码来实现(你需要声明一个bool变量并设置它的值)。
if(alwaysOnTop)
{
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, 0);
}
else
{
SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, 0);
}
最终说明
- 在编辑器中,你将无法看到这些设置。你需要制作一个构建才能看到它们是否有效
- 我强烈推荐在编辑器中用
#if !UNITY_EDITOR将所有代码包裹起来,以防止一些奇怪的问题
评论 (0)