Нужна помощь в динамическом изменении параметров окна программы на Unity для Windows
Вот что должно быть реализовано: Есть 2 сцены. Первая по дефолту настроена как полноэкранная безрамочная. По нажатию кнопки происходит переход на вторую сцену, при этом параметры окна программы должны измениться:
- Окно должно перестать быть полноэкранным, но остаться безрамочным
- Фон окна прозрачный. Должна быть видна только png картинка, расположенная на сцене
- Размер окна должен измениться на 128х128 пикселей
- Расположение окна случайное в пределах экрана (но чтобы не заходило за его границы)
- Окно должно быть всегда поверх других приложений
При нажатии на эту картинку должен произойти возврат на первую сцену, при этом все параметры выше должны вернуться в норму, а именно:
- Окно снова должно стать полноэкранным безрамочным
- При выходе из полноэкранного режима размер окна должен совпадать с расширением экрана и находиться в центре экрана
- Фон окна должен перестать быть прозрачным
- Окно больше не должно быть постоянно поверх других приложений
Вот одна из многочисленных более-менее работающих попыток реализации перехода на вторую сцену, которую я писал не без помощи ии:
using UnityEngine;
using System.Runtime.InteropServices;
using System;
using UnityEngine.SceneManagement;
using System.Diagnostics;
public class GameController : MonoBehaviour
{
[DllImport("user32.dll")]
private static extern IntPtr GetActiveWindow();
[DllImport("user32.dll")]
private static extern bool SetWindowLong(IntPtr hwnd, int nIndex, uint dwNewLong);
[DllImport("user32.dll")]
private static extern uint GetWindowLong(IntPtr hwnd, int nIndex);
[DllImport("user32.dll")]
private static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);
[DllImport("user32.dll")]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
private const uint SWP_NOZORDER = 0x0004;
private const uint SWP_NOACTIVATE = 0x0010;
private const int GWL_EXSTYLE = -20;
private const uint WS_EX_LAYERED = 0x00080000;
private const uint WS_EX_TOOLWINDOW = 0x00000080;
private const uint WS_EX_TOPMOST = 0x00000008;
private const uint LWA_ALPHA = 0x00000002;
private void Start()
{
SceneManager.activeSceneChanged += OnSceneChanged;
}
private void OnSceneChanged(Scene previous, Scene current)
{
if(current.name == "SecondScene")
{
SetWindowProperties();
}
}
public void SetWindowProperties()
{
Screen.SetResolution(128, 128, false);
SetWinsdowPosition(0, 0);
IntPtr hwnd = GetActiveWindow();
uint exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
exStyle |= WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
SetWindowLong(hwnd, GWL_EXSTYLE, exStyle);
SetLayeredWindowAttributes(hwnd, 0, 255, LWA_ALPHA);
}
private void SetWinsdowPosition(int x, int y)
{
IntPtr hwnd = GetActiveWindow();
SetWindowPos(hwnd, IntPtr.Zero, x, y, 128, 128, SWP_NOZORDER | SWP_NOACTIVATE);
}
}
При этом окно всё равно получается корявым и не изменяет большую часть необходимых параметров
Ответы (1 шт):
Чисто ради прикола загнал твой вопрос в DeepSeek. Если не поможет, мой ответ удалю. Просто интересно, может ли ИИ разбирать твои вопросы ;)) Вот ответ дипсика: Ваш код близок к решению, но требует доработок для полного соответствия требованиям. Вот исправленная и дополненная реализация:
using UnityEngine;
using System.Runtime.InteropServices;
using UnityEngine.SceneManagement;
using Random = UnityEngine.Random;
public class GameController : MonoBehaviour
{
// Импорт функций WinAPI
[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")]
private static extern uint GetWindowLong(IntPtr hwnd, int nIndex);
[DllImport("user32.dll")]
private static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);
[DllImport("user32.dll")]
private static extern bool SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
private static extern int GetSystemMetrics(int nIndex);
// Константы WinAPI
private const int GWL_EXSTYLE = -20;
private const uint WS_EX_LAYERED = 0x00080000;
private const uint WS_EX_TOPMOST = 0x00000008;
private const uint WS_EX_TRANSPARENT = 0x00000020;
private const uint LWA_ALPHA = 0x00000002;
private const int HWND_TOPMOST = -1;
private const int HWND_NOTOPMOST = -2;
private const uint SWP_SHOWWINDOW = 0x0040;
private const uint SWP_NOSIZE = 0x0001;
private const uint SWP_NOMOVE = 0x0002;
private const int SM_CXSCREEN = 0;
private const int SM_CYSCREEN = 1;
private static IntPtr hwnd;
private static uint originalStyle;
private static Vector2Int originalResolution;
private void Start()
{
hwnd = GetActiveWindow();
originalStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
originalResolution = new Vector2Int(Screen.width, Screen.height);
SceneManager.activeSceneChanged += OnSceneChanged;
}
private void OnDestroy()
{
SceneManager.activeSceneChanged -= OnSceneChanged;
}
private void OnSceneChanged(Scene prev, Scene next)
{
if (next.name == "SecondScene") SetSecondSceneMode();
else if (next.name == "FirstScene") RestoreFirstSceneMode();
}
public void SetSecondSceneMode()
{
// Настройки камеры для прозрачности (должны быть в префабе второй сцены)
Camera.main.clearFlags = CameraClearFlags.Depth;
Camera.main.backgroundColor = new Color(0,0,0,0);
// Параметры окна
int width = 128;
int height = 128;
Screen.SetResolution(width, height, FullScreenMode.Windowed);
// Случайная позиция в пределах экрана
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
int x = Random.Range(0, screenWidth - width);
int y = Random.Range(0, screenHeight - height);
// Стиль окна
uint newStyle = originalStyle | WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TRANSPARENT;
SetWindowLong(hwnd, GWL_EXSTYLE, newStyle);
// Применение параметров
SetLayeredWindowAttributes(hwnd, 0, 255, LWA_ALPHA);
SetWindowPos(hwnd, HWND_TOPMOST, x, y, width, height, SWP_SHOWWINDOW);
}
public void RestoreFirstSceneMode()
{
// Восстановление стиля окна
SetWindowLong(hwnd, GWL_EXSTYLE, originalStyle);
// Центрирование и полноэкранный режим
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
int x = (screenWidth - originalResolution.x) / 2;
int y = (screenHeight - originalResolution.y) / 2;
Screen.SetResolution(
originalResolution.x,
originalResolution.y,
FullScreenMode.FullScreenWindow
);
// Снятие режима "Поверх всех окон"
SetWindowPos(hwnd, HWND_NOTOPMOST, x, y, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
}
// Для вызова из UI
public void LoadSecondScene() => SceneManager.LoadScene("SecondScene");
public void LoadFirstScene() => SceneManager.LoadScene("FirstScene");
}
Ключевые исправления:
- Прозрачность окна:
Добавлен флаг WS_EX_TRANSPARENT
Камера на второй сцене должна иметь настройки:
-
- Clear Flags: Depth Only
-
- Background: RGBA(0,0,0,0)
- В Player Settings → Resolution and Presentation:
-
- Enable "Resizable Window"
-
- Enable "Use DXGI Flip Model"
- Случайная позиция:
- Используется GetSystemMetrics() для получения размеров экрана
- Корректный расчет позиции с учетом границ экрана
- Режим "Поверх всех":
- Исправлены флаги HWND_TOPMOST/HWND_NOTOPMOST
- Правильное восстановление обычного режима
- Восстановление первой сцены:
- Сохранение оригинальных параметров окна
- Автоматический расчет центрированной позиции
- Корректный выход из полноэкранного режима
- Оптимизация:
- Кэширование handle окна
- Сохранение оригинальных параметров
- Упрощенное управление сценами
Важные требования к сценам:
- Первая сцена:
- Кнопка должна вызывать GameController.LoadSecondScene()
- Камера: Clear Flags = Skybox/Solid Color
- Вторая сцена:
- PNG-объект должен иметь коллайдер
- На PNG-объекте: компонент Button → вызывает LoadFirstScene()
- Камера: Clear Flags = Depth Only, Background = (0,0,0,0)
Дополнительные настройки проекта:
- В Build Settings добавьте обе сцены
- В Player Settings:
- Fullscreen Mode: Fullscreen Window
- Disable "Use Player Log"
- Graphics API: DirectX11 или DirectX12
Это решение гарантирует:
- Полноэкранный безрамочный режим в первой сцене
- Маленькое прозрачное окно во второй сцене
- Случайное позиционирование без выхода за границы экрана
- Корректное восстановление параметров при возврате
- Режим "поверх всех окон" только для второй сцены