SketchのデータをUnity向けに変換するツールを作成

大野友貴人氏(以下、大野):それでは、「UIデザインツールSketchからUnityのUIを作成する」という内容を始めていきます。よろしくお願いします。

私は、KLab株式会社の大野と申します。KLabには5年前に中途入社しました。複数のゲームのプロジェクトに携わった後、現在は基盤を担当する部署で働いています。

今日の内容ですが、KLabでは「Sketch」というUIデザインツールを利用していて、そのSketchのデータをUnity向けに変換するツールを作成しました。作成するために必要になった知識や経験を、今日は紹介したいと思います。

「Sketch」の魅力は軽量で使いやすくUIデザインに特化した機能が豊富なこと

その前にまず、Sketchについて説明をします。SketchはUIデザインツールなのですが、特徴として、UIデザインに特化した機能がいっぱいあるので、画面管理が行いやすく、UIパーツの共通化の概念があります。また、メチャメチャ動作が軽量で、すごく使いやすいです。

これは2020年のどういうUIデザインのツールを使っているかというデータです。1番上に「Figma」があって、その下に「Sketch」があって、「Adobe XD」があります。この3つがUIデザインツールです、下の「Adobe Illustrator」とか「Adobe Photoshop」は汎用的なグラフィックツールです。わりとメジャーなツールです。

Sketchの画面はこのようなイメージです。Unityと似ている部分が少しあって、階層構造で管理しているところや、SymbolというPrefabのような機能もあります。

1からUIを組み上げるコストを減らす「SketchConverter」

今回作ったツールの、「SketchConverter」について説明します。これは実際に動いてるところです。これがSketch上の画面ですね。レイアウトが2つあって、それをUnity側にも変換して持っていけます。

本当に変換されているのかを確認するために、きちんサイズを変えて、1回読み込んで出してみると、きちんと変換されています。簡単なコンポーネントは、変換のタイミングで付けることができます。

仕様の説明ですが、SketchファイルをUnityのGameObjectに変換します。逆はしていないです。Hierarchy上にGameObjectとして出力します。デザイナーさんがこの出力を好きに利用して、一部抜き出したり、Prefabにしたりという想定でやっています。

Sketchの階層構造で、この分割した単位のまま、Unity側にそのままPrefabとして持っていけばいいじゃないかとか、編集状態をこっちで維持したまま再度変換できるようにしたらいいじゃないか、みたいな「もしかしたら、こうしたらもっといいんじゃないか」という話はもちろんあると思います。できる、できないでいうと、がんばればたぶんできるのですが、やった場合、変更差分の適用方法や競合の問題が絶対に起きると思います。

例えば、Sketch側で今まであった階層構造をガツッとなくす。でもUnity側ではその階層構造の部分にコンポーネントを新しく付けて開発するみたいなことがあれば、どっちを優先したらいいのかみたいな問題はあります。

最初にデザイナーさんと決めたのは、1からUIを組み上げるコストを減らすこと目的とするでした。また、SketchとUnityで適切な階層構造はぜんぜん違うので、その調整コストは少しかかってしまうこと、出力したものにもう1回出力は行わないので、変更には弱いことを説明しました。そういう弱点がある程度あることを伝えて、幻想や夢みたいな物語は先に壊しました。

「SketchConverter」の実装

そこまで認識合わせができたら、さっそく作っていきましょう。今日はこれだけ知っていたら、SketchConverterが作れるポイントを絞ってお話しします。

Sketchファイルですが、これはZIP展開できて、中身はJSONなので、メチャメチャ簡単です。画像も入っています。JSONはレイアウト情報とかですね。JSONに含まれてる大量の情報をがんばって読んでいくのは、ちょっと大変ですし、型情報ありでなるべく開発したいと。

そうなった時に、実はGitHubでスキーマが公開されてるので、このGitHubのレポジトリにアクセスしてビルドすると、JSONスキーマを吐き出してくれます。

そのJSONスキーマをさらにC#に吐き出してくれる「quicktype」というツールがあって、これはbrewでインストールしてコマンド叩くだけでできるので、手っ取り早い手段としてお勧めです。今回はこれを使っています。

次ですね。情報は読めるようになったので、階層構造を出力していくことを考えてみましょう。

Sketchの位置階層の情報を見て、基本的には1つのGameObjectを出力すればいいだけですね。それを繰り返せばいいだけなのですが、SymbolというPrefabみたいな機能がSketchにはあって、そこは少し整理が必要です。

これはSketchの画面なのですが、Symbol Masterがあって、これはPrefabそのものみたいなイメージです。対して、このSymbol Instanceは、Prefab、Symbol Masterのほうを使って、さらにHierarchy順に配置したみたいな、PrefabをHierarchy順に配置したようなイメージです。Sketchにはこういう概念があります。

情報の持ち方は、Masterは、階層すべての配置情報が入っています。Instanceは、Rootの配置情報と、どのMasterを使っていますか? というGUIDと、override情報の持ち方になっています。

こうなると変換の手順は、MasterをGameObjectに変換して、Instanceの情報を適用しなければなりません。ポイントは、このMasterの時点でSketchのAnchor設定をRectTransformでしっかり再現する必要があるということです。

さっきの図でもう1回見ると、例えばInstanceのRootの大きさがあって、この要素として星が3つ今並んでますね。この星の配置情報は、MasterのほうでどういうAnchor設定がされているかで決まります。Instance側で大きさ違うので、そことの兼ね合いでこの星は、この場合だと大きさは変わらず、等間隔で並ぶというAnchor設定がされてるので、そういうふうに決まります。

ではそのSketchのAnchor設定がどういうものかを少し説明すると、特に何も考えずにSketch上でObjectをポンと配置した時の挙動がけっこう厄介だったりします。

どういう挙動かというと、自分自身のサイズと、親のサイズの比率が変わらないという挙動ですね。例えば親のサイズを拡大縮小した時に、自分自身のサイズが親とのサイズの割合と維持したままAnchor設定されます。

なので、いつもお世話になっているRectTransformのUIがあると思うのですが、どれを選んでも設定できるものではありません。そういうことをやりたい時は、RectTransformのanchorMinとMaxを使います。ふだんここらへんで設定すると、0か0.5か1ぐらいにしかならないのですが、中途半端な値をしっかり計算で求めてがんばる必要があります。

ここまでで、Sketchの全情報を読んで、その情報をもとにGameObjectを作って、正しいレイアウトを適用するのができたので、ほぼSketchConverterできたようなものです。

あとはここをどうするかですね。Sketchの位置階層をもとに、どういうGameObjectを作るかという、コンポーネントも込みで作るかというところについて、ちょっと考えてみたいと思います。

1つの階層を変換するだけでも、実はやることがいろいろあると思っています。例えば先ほどのRectTransformもそうです。単純なものでいうと、Sketch側の階層面をそのままUnityのGameObjectの名前に反映したりとか。Sketch側が画像やテキストであれば、Unity側でそういうコンポーネント付けなきゃいけないですし、Sketch側にも他にもいろいろと設定項目があります。

ここらへんの細かい実装は、Sketch側の情報をしっかり読んで適切な処理をすればいいだけなので、細かいことは触れませんが、ここで大事なのが、しっかり手を抜かずにクラスに分割しておくということです。

拡張性は高く実装

ここらへんの機能の追加や削除を外から行えるように今回は作りました。ここはけっこう重要で、案件固有の要望が絶対出てくるんですが、そこらへんはコアの実装に手を入れずに対応できます。

また、時間をかけて「うーん」「どういうふうな機能にしよう?」と汎用的な機能を作るよりも、先にガンガン作ってガンガン入れるという運用ができます。

実際に実装を見送りにした機能もあったのですが、なにか言われたら後から追加できるだろうというところです。

実際案件でこういう拡張が行われましたという例をいくつか書いてきました。例えば、これはプラグイン都合でSketch側の階層名がメチャメチャ長くて、管理上しょうがないみたいなパターンがあったのですが、大事な文字列だけを抜き出して、GameObjectの名前にすることで扱いやすくしました。

あと「ここは不要な階層だ」とわかっているところは出力しないようにしたり、Unity側で共通化しているパーツがあればそっちに置き換えてしまうこともできます。

ということで、ここまででSketchの全情報を読んで、全部の階層を作って、階層を作るだけではなくて、いろいろな挙動を実装して、必要があれば後で機能が載せられるようにするところまできたので、これで完成です。

「SketchConverter」を導入した結果

最後にこのツールを投入して半年くらい経つプロジェクトがあるので、そのデザイナーから仕様感をあらためて聞いてきました。

よかったところはやはり、UIを組むのをデザイナーの作業範囲でできたところです。KLabの話になりますが、UIを組む作業はエンジニアがやる場合が多くて、このツールのおかげでそこらへんのハードルがグッと下がりました。また、工数の削減ができたというところもあります。

逆にあまりよくなかったところは、幻想は壊れませんでしたというところです。やっぱりここらへんは気になる部分でした。

今回は1つの事例として何か参考になれば幸いです。今回の内容をきっかけに、ツールができたらぜひ発信してもらいたいと思っています。

参考になるかもしれないので、今回作ったツールはもしかしたらオープンソースにするかもしれません。まだ社内調整中なのでどうなるかはわからないのですが。ということで、以上です。ありがとうございました。

司会者:大野さん、ありがとうございました。アウトゲームの開発でデザイナーさんが用意したレイアウトを、どうUnity側のPrefabに落とし込むかみたいなところが、やっぱり一番時間かかったり、手間だったりするので、すごくいいなと思いました。ぜひオープンソースにしていただけるとうれしいなと思います。

大野:ありがとうございます。ふふふ(笑)、できたらいいですね。ちょっと調整してみます。

司会者:はい。ありがとうございます。それではいくつか質問させていただきたいと思います。1つ目は、デザインツールからUnityのPrefabに落とし込む時に、パフォーマンスの面で特には問題になりませんか。

大野:あまり大きな問題はありませんでした。Sketchを解析する時と、GameObject出力する時の2パターンのどちらかが重いと思うのですが、あまりリアルタイム性が求められるところではなかったので、それほど問題にはなりませんでした。

司会者:ありがとうございます。もう1つ、Sketchの配置情報はすべてAnchor指定なんでしょうか。事前にUnityと基準の解像度設定を合わせている感じですか。

大野:そうですね。合わせることもできるという答え方になりますかね。Sketch側で解像度の設定持っていて、Unity側でも持っていると思うのですが、Sketch側の解像度設定をそのまま持っていくような挙動になるので、そこからさらに調整することはあるかもしれません。