Edited at

Unity+Windows APIで非矩形ウィンドウ


はじめに

Unityで透過ウィンドウを作る話について、2019年2月8日に @wakagomo さんが Unity で Windows の透過ウィンドウを作る(操作も透過) という記事を投稿されていました。

それと同様なものを作っていたのですがなかなか記事を書けていなかったため、解説大変ありがたいです。

一方、完全に透過すると今度は操作できなくなってしまいますので、不透明部分なら操作できる方が自然だと付け足した処理がありましたので、コードと要点を投稿しておきます。


目的

Unityでもデスクトップマスコットなどで利用できる、枠が無いウィンドウを作りたい。


既存手法

Windows Vista 以降でに用意された Desktop Window Manager (DWM) のAPIを用いて、透明に見えるようにはできていた1


課題


  • あくまでウィンドウは矩形(四角)で、透明に見える部分も下のウィンドウの操作はできず、不自然に感じられる挙動となっていた。

  • WS_EX_TRANSPARENT と WS_EX_LAYERED の拡張スタイルを利用することで操作を透過、すなわち下のウィンドウを操作することも(可能ではある)2


    • だが、それでは不透明な部分でもクリックできない。



  • SetLayeredWindowAttributes() 及び UpdateLayeredWindow() が正攻法だが、DWM に比べて次のような問題がある。


    • 手順が複雑

    • パフォーマンス上、不利

    • 単色での抜きとなると、半透明の表現ができず見た目が劣る




提案手法


  • マウスカーソル直下1pxの色を調べる

  • それが透明か否かで、WS_EX_TRANSPARENT + WS_EX_LAYERED による操作の透過を切り替える

マウスカーソル直下の画素を見る方法については、テラシュールブログの記事3を参考にしました。ReadPixels は重そうな印象ですが、そちらで言われているように「1ドット程度なら余裕」と判断しました。

なお、OnPostRender() のタイミングで ReadPixels() を使うのではUIの描画を拾えないため、コルーチンでWaitForEndOfFrame()を利用する形としました。


コード

@wakagomo さんのコードをフォークして編集した TransparentWindow.cs をGistに置きました。このコード使用方法も元のものと同様です。


使用方法


  1. Camera の Clear FlagsSolid Color にする

  2. Camera の Background は R,G,B,A すべてを 0 にする(Aを0にするのが大事!)

  3. TransparentWindow.cs を適当なオブジェクトにアタッチする

※5/30追記 その他に、Player Settings で Run In Background を有効にしておく必要があります。


結果

image.png

Unity 5.6.6 で32ビット実行ファイルとしてビルドしたものを置いておきます。


  • 右クリックで「Quit」という終了ボタンが出ます

  • ドラッグできたりはしません。

透過が自然に動くようになれば、次にはドラッグしたり、大きさを変えたりしたくもなりますね…。


応用

というわけで、他にもいろいろ機能を詰め込んだ UniWinApi というものをGitHubで公開しています。使用例動画が下記です。

VRM viewer sample

ただし実験的にいろいろ詰め込んでいる感があるので、安定しない面はあるかと思います。

何かの参考になればと CC0 にしてありますので、どなたかもっといいものを作ってもらえると嬉しいです。


参考資料