1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Windows11で動くUnityのスタンドアロンプレイヤーのウィンドウの角を設定する機能を作りました

1
Last updated at Posted at 2025-07-23
類似機能のご案内

スタンドアロンプレイヤーではなく、Unity6のエディター自体のウィンドウの角の丸めを抑制したい場合はUnity6のエディターのウィンドウの角を丸めないようにする機能を作りましたをご覧ください

はじめに

Windows11にアップデートしてしばらく経ったのでそこそこ新しいUIに慣れてきました。ですが、UIに関する慣れではどうにもならない問題があります。
それは、「ウィンドウの角が丸いせいでゲームなどで四隅にある表示などが見えなくなる」であったり、「OS経由のスクリーンショットでも角が丸い状態で撮影されるため、丸い角の下が見えてしまったり、その位置に本来ある表示が撮影できなくなる」といったもので、些細なようで結構問題になりそうな事柄だったりします。

本題

そこで、UnityのWindows向けスタンドアロンプレイヤーのウィンドウの角の種類を変更する機能を作りました。導入したプロジェクトをWindows向けにビルドしたプレイヤーのウィンドウのみに作用します。

動作イメージ
上の画像のように、ビルドしたWindows用のプレイヤーの角を任意のタイミングで変えることができます。

検証した環境

  • Windows 11 24H2
  • Unity 2021.3.45f1 / Unity 2022.3.62f1 / Unity 6000.0.53f1
    (いずれのバージョンも、API Compatibility Levelは.NET Standard 2.1、Scripting BackendはMonoとIL2CPPの両方で動作します)

使い方

導入方法

  1. 導入にはGitが必要になります。事前にインストールを済ませてください
    Windows向けのGitの配布サイト

  2. 以下の文字列(Git URL)をコピーする
    https://github.com/HWataame/UnityPlayerWindowCorner.git

  3. 導入するUnityのプロジェクトを開き、Package Managerを表示し、Install package from git URL...を選択する
    導入方法03

  4. 出てきた入力欄に手順2でコピーした文字列を貼り付け、入力欄の右にあるInstallボタンを押す
    導入方法04

  5. Assembly Definition Assetの管理下から使用する場合は、対象のasmdefファイルのAssembly Definition ReferencesにHW.UnityPlayerWindowCornerを追加する
    導入方法05

使用方法

ウィンドウの角の種類を変更する

WindowCorner.Set(WindowCornerType cornerType)メソッド(名前空間HW.UnityPlayerWindowCorner内)を実行すると、スタンドアロンプレイヤーのウィンドウの角の種類を変更できます。

// 角の種類
var cornerType = HW.UnityPlayerWindowCorner.WindowCornerType.DoNotRound;

// スタンドアロンプレイヤーのウィンドウの角の種類を変更する
HW.UnityPlayerWindowCorner.WindowCorner.Set(cornerType);

ウィンドウの角の種類は、

ウィンドウの角の種類 外見 備考
WindowCornerType.Default 丸い角 OSに委任する 24H2時点ではWindowCornerType.Roundと同じ外見になる
WindowCornerType.DoNotRound 四角い角 Windows10時代のような外見になる
WindowCornerType.Round 丸い角 24H2時点では未指定時(WindowCornerType.Default)と同じ外見になる
WindowCornerType.RoundSmall 丸い角 WindowCornerType.Roundよりも丸みが小さい

の4種類から設定できます。それぞれは以下の画像のような見た目になります。
角の種類

利用例:アプリケーション起動時に角を四角くする

Windowsのビルドに載るソースコードに

[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void ProcessWindowCornerOnStartup()
{
    WindowCorner.Set(WindowCornerType.DoNotRound);
}

と記述する(メソッド名やアクセス修飾子はお好みで変更可)と、スタンドアロンプレイヤーのウィンドウの角を最初から四角くすることができます。

非対応環境での実行時の警告ログを設定する

この機能はWindows11で動作するスタンドアロンプレイヤーを対象にしているため、当然ながら他の環境では何も起こりません。
非対応の環境(特にWindows上で動作するUnityEditor)でもWindowCorner.Setが呼び出されていることを確認できるようにするため、非対応の環境では実行すると警告が出るようにしています(以下の画像は、Windows上で動作するUnityEditorのPlayModeで呼び出した時のものです)。
警告(WindowsのUnityEditor)

場合によっては、警告の出る仕様が煩わしくなることもあるかと考え、警告ログの出力を任意に設定できるようにしました。
WindowCorner.IsOutputLogプロパティ(名前空間HW.UnityPlayerWindowCorner内)にfalseを指定すると、警告ログを出力しないようにできます(出力を再開させたい場合は同プロパティにtrueを指定します)。

実装について

概要

この機能は、すべてC#で記載されていて、WINAPIをP/Invokeで呼び出すことによってウィンドウの角を操作しています。
また、使用しているAPIがおおよそUnityEditorのウィンドウの角を操作する機能の時と同じであるため、重複する部分は簡潔に説明します。

ウィンドウの角の種類を列挙型にする

DwmSetWindowAttribute関数には4バイトの整数値への参照を渡せばいいため、内部値の型がuintの列挙型を定義します。今回はWindowCornerTypeとして定義しました。

外見 対応するC++使用時の表記
0 角の外見をOSに委任する(24H2時点では丸い角(2)) DWMWCP_DEFAULT
1 四角い角 DWMWCP_DONOTROUND
2 丸い角 DWMWCP_ROUND
3 丸い角(2)よりも丸みが小さい角 DWMWCP_ROUNDSMALL

ウィンドウの角を操作する

DwmSetWindowAttribute関数を呼び出します。この関数のシグネチャは

// WINAPI(C++)での定義
HRESULT DwmSetWindowAttribute(HWND, DWORD, LPCVOID, DWORD);

なのですが、今回はウィンドウの角の操作にしか使用しないため、先述の列挙型を使用して

// C#側でDwmSetWindowAttributeをウィンドウの角の操作のために呼び出すための定義
[DllImport("dwmapi.dll", CallingConvention = CallingConvention.Winapi)]
static extern uint DwmSetWindowAttribute(nint windowHandle, uint attribute, ref WindowCornerType cornerType, uint dataSize);

と定義します。

型の読み替えについて

  • 戻り値のHRESULTは32ビット整数型であるため、今回はuintに読み替える1
  • 第1引数のHWNDは(今時あまり気にすることはないですが)ビルド対象のビット数で型のサイズが変わるため、nintに読み替える
  • 第2、第4引数のDWORDは32ビット符号なし整数型であるためuintに読み替える
  • 第3引数のLPCVOID(C++ではconst void*と等価)は本来であればnintに読み替えるが、今回はウィンドウの角の操作にしか使用しないため、この用途では32ビット整数値へのポインタを渡せばよい。そのため特例で先ほど定義した32ビット整数型の列挙型を参照渡しにしたref WindowCornerTypeに読み替える

スタンドアロンプレイヤーのウィンドウを取得する

ウィンドウの角を操作する方法を用意しても操作する対象がなければ意味がないため、スタンドアロンプレイヤーのウィンドウを取得します。とは言ってもUnity自体にはプレイヤーのウィンドウハンドルを取得する機能が公開されていないため、

  1. GetCurrentProcessId関数を呼び出して自身のプロセスIDを取得しておく
  2. EnumWindows関数を呼び出して現在のシステムに存在するトップレベルのウィンドウをすべて列挙する
  3. GetWindowThreadProcessId関数を呼び出して、列挙されたウィンドウハンドルを生成したプロセスのIDを取得する
  4. 2.で取得した自身のプロセスIDと比較する

という処理の流れをとります。3.と4.はEnumWindowsのコールバック関数内での処理で、列挙されるウィンドウハンドルの数ぶん繰り返されます。
このとき、自身(プレイヤー)と列挙されたウィンドウのプロセスIDが同じであれば、そのウィンドウはプレイヤーのプロセスが生成したものであるため、そのウィンドウに対して角の操作を行います。
これらの処理で使用する関数のC#での定義は以下のようにします。
(また、EnumWindowsなどに出現するSetWindowCornerCallbackParametersは、今回必要な値をコールバック関数に渡すために定義したref構造体で、自身のプロセスID(uint)と、設定する角の種類(WindowCornerType)の2種類のフィールドを含みます)

// 1. 現在のプロセスのIDを取得する
[DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi)]
static extern uint GetCurrentProcessId();

// 2. EnumWindowsのコールバック関数のデリゲート
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
delegate bool EnumWindowsProc(nint windowHandle, ref SetWindowCornerCallbackParameters parameters);

// 2. トップレベルのウィンドウを列挙する
[DllImport("user32.dll", CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnumWindows(EnumWindowsProc callback, ref SetWindowCornerCallbackParameters parameters);

// 3. ウィンドウを生成したプロセスのIDを取得する
[DllImport("user32.dll", CallingConvention = CallingConvention.Winapi)]
static extern uint GetWindowThreadProcessId(nint windowHandle, out uint processId);

型の読み替えについて(追加説明分のみ)

  • EnumWindowsの第1引数はWNDENUMPROCで、HWNDLPARAMを引数に持ち、BOOL値を返す関数ポインタの型である。関数ポインタは等価の引数と戻り値の組み合わせのデリゲートで読み替えることができるため、今回は同等の引数と戻り値型のデリゲート型を定義し、そのデリゲート型で読み替えた
  • デリゲート型とEnumWindowsの第2引数のLPARAMはポインタ型で、本来であればnintに読み替えるが、今回はパラメーターを渡す必要があるため、構造体の参照渡しとして読み替えている。なお、今回使用する構造体はref構造体であるため参照が示す先は必ずスタックになる

ネイティブ関数のコールバックにC#のメソッドを渡す時の注意事項

MonoなどのJIT環境で動かしているうちは問題ないのですが、IL2CPPでビルドした場合は対策を講じないとコールバックを渡そうとすると例外が出ます。[AOT.MonoPInvokeCallback(System.Type)]属性をコールバックに渡すメソッドに付与し、その属性でコールバックの型を指定するとIL2CPP環境でも動くようになります。

ライセンス

MITライセンスです。

リポジトリ

  1. 例外が出ることを許容する設計である場合ば[DllImport]属性にPreserveSig = falseを指定して、戻り値をvoidにする定義も可能です。その場合は失敗時に例外が発生するようになります。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?