🤖

UnityFigmaBridgeを数ヶ月使ってみて

2023/08/15に公開

はじめに

現在所属しているプロジェクトでは、Figmaで作成した画面を、UnityFigmaBridgeを使ってUnityの画面として使っています。

UnityFigmaBridgeを数ヶ月使ってみて、良いところ、悪いところ、工夫しているところについてまとめようと思います。

こちらはUnityFigmaBridgeを使い始めたころにかいた記事です。

環境

Unity 2022.3.5f1
UnityFigmaBridge 1.0.8

UnityFigmaBridgeがSync Documentで出力してくれるPagesは使っていません。
またPrototype Flowも未使用です。

良いところ

再現度が高い

Figmaで作った画面はサイズから位置まで、ほぼその通りにUnity上で再現してくれます。
Figma上でSafeAreaという名前でオブジェクトを作っておけば、Unity上でSync Document時にSafeAreaコンポーネントを自動で付与してくれます。
Figma上で複数の画面のアスペクト比に対応しておけばUnity上でそのまま動いてくれるのは助かります。

コンポーネントの付与

ComponentsやScreensにあるPrefabと同じ名前のクラスがあれば、Sync Document後にコンポーネントを付与してくれます。

こちらの記事にあるように、変数とバインディングする仕組みを導入して、付与されたコンポーネントでAuto Assignを実行しています。
UnityFigmaBridgeにも変数への自動バインドの仕組みがあるのですが2階層までしか対応していないため自作で対応しています。

フォントのMaterial Presetを自動で作ってくれる

Figma上で同じフォントを使用していても、装飾などが異なる場合、Unity上でフォントのMaterial Presetを自動で作ってくれます。
ここを手動で対応するのは大変なのでとても助かります。

悪いところ

Sync Documentが重い

画面数や画像などが増えてきて、Sync Documentをするたびに数分かかるようになってきました。
これはSync Documentで全てのScreenやコンポーネントを全て一から作り直しているからです。

作者の方が将来的には変更されたPageやScreenのみ更新するようにしたいとおっしゃっていたので、そのうち高速化されると思います。

ローカルでの変更は破棄される

ComponentsやPagesやScreensの下にあるprefabに変更を加えても、Sync Documentするとその変更は破棄されます。
UIパーツをアニメーションさせたい場合などにAnimatorをつけても消えてしまうので、再度つける必要があります。

Buttonコンポーネントがついてしまう

Figma上でオブジェクト名にButtonという文字が入っていると、自動的にButtonコンポーネントが追加されます。
ButtonはSEなどの対応も必要なので独自に作成したクラスで処理したいのでButtonコンポーネントがつくと困ります。
デザイナーと相談して、ButtonはBtnという名前にすることで運用回避しています。

FigmaImage

FigmaImageはFigma上で矩形ツールなどを使って表現したものをUnity上で再現してくれます。
こちらは動的に「Figma/FigmaImageShader」を使って描画されるため、Shader Graphなどで作ったShaderに差し替えるのが厳しいです。

画像に対して何かしらの効果をかけたい場合は、FigmaImageにならないようにFigma上で気を付ける必要があり手間です。

工夫しているところ

Sync終わったら押してボタン


同僚にはボタンの名前で笑われましたが、デザイナーからは分かり易いからいいねと好評でした。

このボタンを押すことで、Sync後の手作業を可能な限り自動化させています。

自動化対応の一部を下記に記します。

変数へのバインディング

ComponentsとScreensの下に作成されるprefabと同じ名前でクラスを作成しておき、変数とバインディングされるようにします。

Sync終わったら押してボタンでAutoAssign()が動くようにしておきます。
ComponentsとScreensにあるprefabを検索して、Viewファイルを継承したクラスであれば変数へのバインディング処理を実行します。

static void AutoAssign()
{
  // ComponentsとScreensのprefabのGuidを取得
  var componentPrefabGuids = AssetDatabase.FindAssets("t:Prefab", new[] { "Assets/Figma/Components" });
  var screenPrefabGuids = AssetDatabase.FindAssets("t:Prefab", new[] { "Assets/Figma/Screens" });

  try {
    AutoAssignCore(componentPrefabGuids);
    AutoAssignCore(screenPrefabGuids);
  }
  finally {
    AssetDatabase.SaveAssets();
    AssetDatabase.Refresh();
  }
}

static void AutoAssignCore(IEnumerable<string> prefabGuids)
{
  foreach (var figmaPrefabGuid in prefabGuids) {
    // Guidからpathを取得
    var assetPath = AssetDatabase.GUIDToAssetPath(figmaPrefabGuid);
    // pathからprefabをLoad
    var contentsRoot = PrefabUtility.LoadPrefabContents(assetPath);
    // AutoAssign実行
    EditorUtil.AutoAssign(contentsRoot);

    PrefabUtility.SaveAsPrefabAsset(contentsRoot, assetPath);
    PrefabUtility.UnloadPrefabContents(contentsRoot);
  }
}

EditorUtil.cs

public static void AutoAssign(GameObject rootGameObject)
{
  var components = rootGameObject.GetComponents<Component>();
  foreach (var component in components) {
    // フィールドへのAssign処理
    AutoAssign(component, rootGameObject);
  }

  foreach (var view in rootGameObject.GetComponents<View>()) {
    // 個別の特別対応はここでやる
    view.OnAutoAssign();
  }
}

OnAutoAssignメソッドは、UnityFigmaBridgeの動作ではどうにもならない場合の独自処理を実装するために用意しています。
先ほどのAnimatorをつけても消えてしまうような場合など、このViewのこのオブジェクトにはXXXAnimatorをつけるなどの処理をさせることができます。

文字間隔など

処理はさきほどの「変数へのバインディング」とほぼ同等ですが、prefab内からTextMeshProを検索して、文字間隔を-4にしていたりします。
UnityFigmaBridgeは-0.7で文字間隔を入れてくるのですが、自分のプロジェクトが使っている日本語の文字フォントは-4が良さそうでした。

またオブジェクト名に「@Wrap」という文字が入っていたら文字の折り返しありになるようにしていたりします。

var textMeshProUguis = contentsRoot.GetComponentsInChildren<TextMeshProUGUI>();
foreach (var textMeshProUgui in textMeshProUguis) {
  textMeshProUgui.characterSpacing = -4f;
  var textMeshProUguiObjectNames = textMeshProUgui.gameObject.name.Split("@");
  if (1 < textMeshProUguiObjectNames.Length && textMeshProUguiObjectNames[1..].Contains("Wrap")) {
    textMeshProUgui.enableWordWrapping = true;
  }
  else {
    textMeshProUgui.enableWordWrapping = false;
  }
}

ダミー画像

特定の画像はプログラムからIDを元に取得され表示されるような場合があります。
そのような場合でも、Figma上では見た目の確認のためになんらかの画像を置いておきたいという要望がありました。

先ほどの文字の折り返し同様に「@Dummy」という文字が入っていたら、そのオブジェクトは削除しています。

prefab variant

「Sync終わったら押してボタン」で対応していくのもかなり大変ですし、prefabへの変更がどうにかして保持できないかを考えました。
Screenの下にあるprefabから、prefab variantを作成して、UnityFigmaBridgeに消されない場所へ移動しておきます。

作ったprefab variantへコンポーネントを付与したりして変更を加えます。

ここでSync Documentをすると、Screenの下のprefabは一度全て削除されるため、file IDが変わってしまい、prefab variantからの参照も切れるため、加えた変更もなかったことになってしまいます。

prefab variantへの変更を別ファイルに保存して、Sync後に復帰させるようにしました。

まとめ

いろいろと苦労もありますが、FigmaからUnityの画面を作るようにしたのは良かったと思います。

いきなりUnityで画面を作り始めるということはないと思いますし、以前のプロジェクトではExcelやPSDなどに画面イメージが作成されて、それをエンジニアがみながらUnity上でパーツ配置してデザイナーに確認をとっていました。
どこかにはUnityで作成される画面のベースとなるイメージがまとまっていることは必要で、Figmaは軽量で使いやすく、多人数での同時作業や動きのある画面が作れることでデザイナーにもとても評判がよく、画面イメージを作ることに適していると思います。

今後心配な点としては、現時点では、FigmaからSyncした後、いろいろ工夫をしてローカルの変更を復帰できています。
しかし、もっと込み入った処理がprefabに追加されていったら、いつか復帰できなくなりそうです。

どこまでをFigmaで対応して、どこからはUnityローカルの手作業で対応していくかみたいなのはあらかじめプロジェクトで決めておいたほうが良さそうです。

例1)
・FigmaからUnityへのSyncは画面のUIパーツの配置のみ。以降の対応はUnityで直接行う。
・追加のUIパーツがあった場合でもSyncは実施しない。

例2)
・FigmaとUnityは常にSyncで同期してもよい状態とする。
・Figma上で対応できない項目に関しては、先ほどのOnAutoAssignメソッドのような仕組みを使って自動で復帰できるようにするか、手作業で復帰させる。

Happy Elements

Discussion