📊

2年かけてUnityでuGUIやUI Toolkitの代わりに使うGUIライブラリを開発して公開した

に公開
2

はじめに

UnityのuGUIやUI Toolkitを使っていて、「もっとシンプルにコードでGUIを組みたい」と思ったため、RimGuiという自分の理想に合ったGUIライブラリをゼロから開発して公開しました。

https://gridrand.com/rimgui/WebGL/

https://assetstore.unity.com/packages/tools/gui/rimgui-316805

main

自分の理想のGUI

  • コードでGUIを組みやすい
  • 簡単にGUIを作成できる
  • 実用性重視
    • 派手さは必要ない
  • デバッグや開発時だけでなく、本番のゲームでも使える
  • 高速
  • Unityへの依存が少ない
    • 拡張、カスタムしやすくするため
    • 実装の中身が見通しやすく、理解しやすくなる
    • パフォーマンスチューニングをしやすくなる

既存のGUI

uGUI

  • 安定している
  • コードだけで完結するUI構築は困難
  • 大量のUIだと重い
  • 動的な更新があると重い

UI Toolkit(ほとんど使ったことがないので的はずれかも)

  • スタイリングしやすい
  • ボイラープレートが多い
  • スクリプトからの制御がしづらそう
  • 個人的にHTMLやCSSを知らないため、あまりメリットを感じない。

Unity IMGUI

  • コードだけでUIを作成できる
  • runtimeで使うにはパフォーマンスが厳しい
  • 複数解像度への対応が難しい

開発したGUIライブラリ

Immediate-Mode GUIとなっており、シンプルで直感的に使えるように開発しました。
ライブラリ名はImmediate-ModeからImを取り、Rを付けてRimGuiと名付けました。

Built-In,URP,HDRPに対応しており、PCやMobile,WebGLでも動作します。

使い方

以下のように、メソッドを呼ぶだけでGUIを構築できます。

Gui.Heading("Sample");
Gui.LabelSlider("Slider", ref value, 0, 100);
if (Gui.Button("Increment"))
    value++;

sample-ui

  • Heading():見出しを表示
  • LabelSlider():ラベル付きスライダーを表示し、値を制御
  • Button():ボタンを表示し、クリックされたらtrueを返す
    • Dear ImGuiなどの多くのImmediate-Mode GUIと同じ

他にも多くのWidgetや機能が存在します。

  • Box
  • Button
  • ToggleButton
  • RadioButton
  • CheckBox
  • Color
  • ColorPicker
  • Custom
  • Dropdown
  • Foldout
  • Frame
  • InputText
  • Label
    • LabelText, LabelInput, LabelNumeric, and more
  • ListView
  • Scroll
  • Separator
  • SlidePad
  • Slider
  • Sprite
  • Splitter
  • Tab
  • Text
  • Texture
  • Tree
  • Window
  • Drag and Drop
    などなど

詳しくは下のリンク先を参考にしてください。
https://gridrand.com/rimgui/docs/introduction

フォントアトラスの生成

TextMeshProを使わず、独自にフォントアトラスの生成およびレンダリングを行っています。
フォントのラスタライズには、stb_truetypeのC#移植版を使用しています。

atlas-font-editor

Unityには FontEngine というクラスが存在しますが、重要なメソッドがinternalとなっており、使用するとクラッシュすることが多くありました。
また、ラスタライズ時の座標も不明瞭だったため採用を見送りました。

とはいえ内部ではJobSystemを活用しており、基盤にはおそらくFreeTypeが使われていることから、高いパフォーマンスが期待できます。
そのため、クラッシュの原因や座標の仕様が明確になれば、採用も十分検討に値すると考えています。

描画

SetPass callsを抑えるために、Shader、Materialを1つにしています。

Unityではさまざまな手法でレンダリングが可能ですが、RimGuiではCommandBufferを使用しています。これは、特定の領域をフレームバッファから切り離して描画する必要があるためです。

また、CommandBuffer.DrawMesh()を用いた描画では、たとえ同じMaterialを使っていても、StatisticsビューではSetPass callsが増加しているように見えますが、実際にはGPUへのステート変更は行われていないため、問題はありません。

Opening up RenderDoc capture shows that, if the material is unchanged between DrawMesh calls, all that is being fired off to the GPU are calls to set vertex/index buffers and then a draw call…it looks like the profiler is wrong. I don’t think that Unity is sending state changes to the GPU unless it has to.
Is there a way to batch meshes that are drawn with CommandBuffer.DrawMesh? - Unity Forum

Text

SDFかBitmapかで迷いましたが、SDFでTextを描画しています。

SDFのメリット

  • 高DPIだと綺麗
    • モバイルだと特に顕著
  • Fontのサイズがいくつでも1つのアトラスで対応可能
  • 今後、影などのエフェクトを描画できるように改修できる

Bitmapのメリット

  • ラスタライズが速い
    • ダイナミックにテクスチャアトラスに書き込む場合に重要
  • Shaderがシンプル

謝辞

以下のライブラリは、本実装にあたって非常に参考になり、多くのインスピレーションを頂きました。
この場を借りて、心より感謝申し上げます。

最後に

結果として開発に2年以上かかってしまいましたが、最初はこんなにも時間をかけるつもりはありませんでした。
使いやすくするためにある程度できてから大部分を改修したため、ここまで時間が掛かってしまいましたが、使いやすいGUIライブラリになったと思います。
ただ、lineやtriangleのアンチエイリアス化など、まだ足りない機能があるため、今後も開発を続けていきます。

ご意見やご要望などありましたら、ご連絡いただけると嬉しいです。

Discussion

akeit0akeit0

ご存知か分かりませんが、2年前に近いプロジェクトをつくっていたので、使いやすくすること及び完成が大変なのはとても分かります...!
(自分は途中でやめちゃったので)
https://zenn.dev/aakei/articles/0ee44116709f93
買うかは正直分かりませんが、応援します!

tataroutatarou

ありがとうございます!
ZimGuiのコードは参考にさせていただきました。
そちらのページも何回も読んでいました。
かなり前なので曖昧ですが、私のライブラリのWindowの右下の三角形はZimGuiの名残りだと思います!