윈도우에서 원하는 모니터의 정보를 받아온 후 스크린샷 촬영 기능 추가

유니티 윈도우 다크 테마 및 DOTween 에셋 추가
This commit is contained in:
2026-01-13 02:13:18 +09:00
parent efd9f9e4f5
commit 051a71ac15
56 changed files with 6274 additions and 44 deletions

View File

@@ -1,64 +1,153 @@
using UnityEngine;
using System;
using System.IO;
using System.Windows.Forms;
using System.Drawing;
using System.Windows.Forms; // 모니터 식별 및 스크린 정보
using System.Drawing; // Bitmap 관련
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using TMPro;
using Unity.VisualScripting;
using Application = UnityEngine.Application;
using Screen = System.Windows.Forms.Screen;
using Graphics = System.Drawing.Graphics;
public class TakeScreen : MonoBehaviour
{
// 캡처할 모니터 인덱스 (0 = 첫번째 모니터)
public int monitorIndex = 0;
// 캡처할 모니터 인덱스 (2 = 첫번째 모니터)
public int selectMonitorIndex = 2;
public int takeNumber;
int monitorIndex = 0;
MONITORINFO selectMonitor;
int selectWidth;
int selectHeight;
[SerializeField]
TMP_InputField inputPrefix;
[SerializeField]
TMP_InputField inputResult;
[SerializeField]
DirectorySelect directorySelector;
// WinAPI 구조체
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left, top, right, bottom;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct MONITORINFO
{
public int cbSize;
public RECT rcMonitor;
public RECT rcWork;
public int dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string szDevice;
}
// WinAPI 델리게이트
private delegate bool MonitorEnumProc(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData);
// WinAPI 함수
[DllImport("user32.dll")]
private static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, MonitorEnumProc lpfnEnum, IntPtr dwData);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi);
[DllImport("user32.dll")]
private static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("gdi32.dll")]
private static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("gdi32.dll")]
private static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int width, int height);
[DllImport("gdi32.dll")]
private static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
[DllImport("gdi32.dll")]
private static extern bool BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight,
IntPtr hdcSrc, int nXSrc, int nYSrc, CopyPixelOperation dwRop);
[DllImport("gdi32.dll")]
private static extern bool DeleteObject(IntPtr hObject);
[DllImport("gdi32.dll")]
private static extern bool DeleteDC(IntPtr hdc);
[DllImport("user32.dll")]
private static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
private void Start()
{
// 연결된 모니터 목록
Debug.Log ("displays connected: " + Display.displays.Length);
// Display.displays[0] is the primary, default display and is always ON, so start at index 1.
// Check if additional displays are available and activate each.
for (int i = 1; i < Display.displays.Length; i++)
{
Display.displays[i].Activate();
}
Debug.Log("=== 모든 모니터 정보 ===");
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, MonitorCallback, IntPtr.Zero);
}
void Shot()
private bool MonitorCallback(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData)
{
CaptureMonitor(monitorIndex, directorySelector.selectedFolderPath);
Debug.Log($"Monitor {monitorIndex} 캡처 완료 : {directorySelector.selectedFolderPath}");
}
void CaptureMonitor(int index, string savePath)
{
// 연결된 모니터 목록
Screen[] screens = Screen.AllScreens;
MONITORINFO mi = new MONITORINFO();
mi.cbSize = Marshal.SizeOf(typeof(MONITORINFO));
monitorIndex++;
if (index < 0 || index >= screens.Length)
if (GetMonitorInfo(hMonitor, ref mi) && monitorIndex == selectMonitorIndex)
{
Debug.Log("잘못된 모니터 인덱스입니다.");
return;
}
// 해당 모니터 정보
Screen targetScreen = screens[index];
Rectangle bounds = targetScreen.Bounds;
// 비트맵 생성
using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
{
using (Graphics g = Graphics.FromImage(bitmap))
{
g.CopyFromScreen(bounds.X, bounds.Y, 0, 0, bounds.Size);
}
int width = mi.rcMonitor.right - mi.rcMonitor.left;
int height = mi.rcMonitor.bottom - mi.rcMonitor.top;
bool isPrimary = (mi.dwFlags & 1) != 0;
// png로 저장
bitmap.Save(savePath, ImageFormat.Png);
Debug.Log(
$"{mi.szDevice} 모니터 \n" +
$" - 해상도 : {width} x {height}\n" +
$" - 위치 : x = {mi.rcMonitor.left}, y = {mi.rcMonitor.top}\n" +
$" - 주 모니터 여부 : {isPrimary}\n"
);
selectMonitor = mi;
selectWidth = width;
selectHeight = height;
}
return true; // 계속 나머지 모니터 순회
}
public void Take()
{
string fileName = $"{inputPrefix.text}_{string.Format("{0:D2}", takeNumber)}.png";
string savePath = Path.Combine(directorySelector.selectedFolderPath, fileName);
Shot(selectMonitor.rcMonitor.left, selectMonitor.rcMonitor.top, selectWidth, selectHeight, savePath);
}
void Shot(int x, int y, int width, int height, string savePath)
{
IntPtr desktopDC = GetDC(IntPtr.Zero);
IntPtr memoryDC = CreateCompatibleDC(desktopDC);
IntPtr bitmap = CreateCompatibleBitmap(desktopDC, width, height);
IntPtr oldBitmap = SelectObject(memoryDC, bitmap);
BitBlt(memoryDC, 0, 0, width, height, desktopDC, x, y, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt);
using (Bitmap bmp = Image.FromHbitmap(bitmap))
{
bmp.Save(savePath, ImageFormat.Png);
}
// 정리
SelectObject(memoryDC, oldBitmap);
DeleteObject(bitmap);
DeleteDC(memoryDC);
ReleaseDC(desktopDC, memoryDC);
inputResult.text = savePath;
}
}