本ページにはプロモーションが含まれます
Unity

【Unity】TextMeshProのマテリアルがピンク色になってテキストが表示されない

この記事は約7分で読めます。

はじめに

後でしったのですが、この現象は、インスタンス化時のマテリアル参照ロストということらしいです。
ロードしたシーンのゲームオブジェクトにAddressableで読み込んだプレハブを配置してそこに別のプレハブインスタンスをクローンする処理がある。
クローンしたプレハブ内のTMPのテキストは正常に表示されるのだが、ロードしたシーンのゲームオブジェクトにAddressableで読み込んだプレハブのTMPのテキストのマテリアルがピンク(要はシェーダが参照できない)状態で調査解決に丸一日かかったのでその対応メモ。

原因が分からず…

ピンク問題なら解決法はWEBでよくみたので、すぐ終わるだろうとおもったが、時間がどんどん過ぎていく。検証のため18時間くらいトライ&エラー。手首が腱鞘炎でひどく痛い。シェーダーをエディタ上で強制的に読み込ませても、設定リセットしても、SDFもシェーダもAddressableにインクルードしても、何をしてもダメだった。原因がさっぱりわからない。
わかるのは、マテリアルからシェーダーの参照が外れている=参照を戻せば表示されるはず。

Addressableでプレハブ読み込み→GameObject.Instantiate
多分この時点でプレハブ内のTMPのシェーダー参照が切れる。
↓のようにInstantiateしたオブジェクト直下のすべてのTMPのフォントマテリアルにシェーダーを再設定してみる。

  // 再設定するシェーダ
  Shader tmpShader = Shader.Find("TextMeshPro/Distance Field");
  
  // TextMeshProのマテリアルを設定  
  var textMeshProComponents = instantiatedObject.GetComponentsInChildren<TMPro.TextMeshProUGUI>();
  foreach (var textMeshPro in textMeshProComponents)
  {
      // デフォルトマテリアルを取得
      Material defaultMaterial = textMeshPro.fontMaterial;
      if (defaultMaterial != null)
      {
          if (tmpShader != null)
          {
              // シェーダーを再設定
              defaultMaterial.shader = tmpShader;
              Debug.Log($"シェーダー '{tmpShader.name}' を再設定しました。");
          }
          // 元のマテリアル名をログに出力
          Debug.Log($"TextMeshProオブジェクト '{textMeshPro.name}' のデフォルトマテリアル: {defaultMaterial.name}");
          // マテリアルのクローンを作成
          Material newMaterial = new Material(defaultMaterial);
          // 新しいマテリアルを設定
          textMeshPro.fontMaterial = newMaterial;
          // 再設定したマテリアル名をログに出力
          Debug.Log($"TextMeshProオブジェクト '{textMeshPro.name}' に再設定されたマテリアル: {newMaterial.name}");
      }
  }

行けた…!連休の時間返せー!

謎なのは、この表示されない方のプレハブにさらにAddressableでプレハブクローン追加したものの方はTMPのテキストは正常に表示されてるのよね……。Instantiateで参照が外れると思ってたんですが、そうでもなく、気持ち悪いが連休も終わるので原因がわからないままいったん放置。

GameObject.Instantiate でマテリアルの参照が外れる可能性について考えると、以下の外的要因が関係していることが多いそうだ。Instantiate 自体が参照を外すことは少ないが、他の問題が原因で発生することがあるのかも。

1. Addressablesの依存関係不足

  • 可能性: Addressablesを使ってプレハブをロードする際、そのプレハブが依存しているマテリアルやシェーダーが正しくロードされないことがある。これが原因で、インスタンス化されたオブジェクトのマテリアルやシェーダーが正しく参照できず、ピンク表示になる可能性があるのかも。
  • 原因: Addressablesはプレハブ単位でロードするが、プレハブに使われているマテリアルやシェーダーも一緒にロードされていないと依存関係が解決されない。
  • 解決策: プレハブに依存するリソース(マテリアル、シェーダー)もAddressablesに含まれているか確認し、正しくロードされるように設定する必要がある。

2. シェーダーがビルドに含まれていない

  • 可能性: ビルド時にシェーダーが正しく含まれていないと、インスタンス化したオブジェクトのマテリアルがシェーダーを参照できず、ピンク表示になるかも。
  • 原因: Unityは未使用のシェーダーをビルドから除外するため、特定のカスタムシェーダーがビルドされていない可能性がある。
  • 解決策: Graphics Settings の「Always Included Shaders」に必要なシェーダー(例: TextMeshPro/Distance Field)を追加して、ビルドに含まれるようにする必要がある。

3. マテリアルの共有による問題

  • 可能性: インスタンス化されたオブジェクトがオリジナルと同じマテリアルを共有している場合、他のオブジェクトによってそのマテリアルが変更されたり削除されたりすることで参照が外れることがあるのかも。
  • 原因: Unityのデフォルト動作では、Instantiate するとオリジナルのオブジェクトと同じマテリアルを共有するので、別のインスタンスがそのマテリアルを変更すると、他のインスタンスにも影響が出るらしい。
  • 解決策: 各インスタンスで独自のマテリアルを使う場合は、new Material(originalMaterial) でマテリアルを複製し、インスタンスごとに別々のマテリアルを割り当てると良いかも。

4. リソースのロード順序や依存関係の不整合

  • 可能性: ランタイム中にアセットがロードされるタイミングや順序が適切でないと、マテリアルやシェーダーが正しくロードされず、参照が外れることがあるのかも。
  • 原因: シーンの他のオブジェクトやリソースがロードされる前にインスタンス化されると、依存しているリソースが正しく使えない場合があるらしい。
  • 解決策: リソースのロード順序や依存関係を慎重に管理し、Addressablesで依存関係を明確にすると良いかも。

まとめ

GameObject.Instantiate でマテリアルの参照が外れるのは、主に以下の 3つの要因 が関係している可能性がある。

  1. Addressablesの依存関係管理不足
  2. シェーダーがビルドに含まれていない
  3. マテリアルの共有による外部の影響

これらの問題を確認・解決することで、ピンク表示の問題を防ぐことができるようだ。

おわりに

追記:現場のクライアントエンジニアさんいわく、本質的な解決(このコードなしで直す)には、「Font Assetのデフォルトマテリアルを使わず、明示的に作成したMaterial Presetをアサインしておく」などの対策が必要になるとのこと。今回のような動的生成時はこの「再割り当て処理」を入れるのが最も確実で低コストな修正方法だそうです。

「Font Asset内のデフォルトマテリアル」ではなく、「明示的なMaterial Preset」を使用する手順は以下の通りです。 これにより、プレハブが「ファイルとして実体のあるマテリアル」を直接参照するようになるため、ロード時の参照切れ(ピンク色化)を防ぐことができます。

手順

  1. デフォルトマテリアルを見つける Projectウィンドウで、使用している Font Asset(例: NotoSansJP-Regular SDF)の左横にある「▶」を押して展開します。 中に「Font Material」という名前(またはフォント名と同じ名前)のマテリアルが入っています。
  2. マテリアルを複製(実体化)する そのマテリアルを選択し、Ctrl + D(複製)を押します。 すると、Font Assetの外に新しいマテリアルファイル(例: NotoSansJP-Regular SDF 1.mat)が作成されます。 ※ これが「Material Preset」となります。わかりやすい名前(例: RefStageMenu_TextMat など)に変更しても良いでしょう。
  3. プレハブに割り当てる RefStageMenu のプレハブを開きます。 ピンク色になりやすい TextMeshProUGUI コンポーネントの Inspector を見ます。 Main Settings > Material Preset の欄に、先ほど作成したマテリアル をドラッグ&ドロップして割り当てます。

なぜこれで直るのか?

修正後: プレハブは「プロジェクトフォルダにある .mat ファイル」を直接参照します。これは通常のテクスチャやモデルと同じ扱いになるため、Unityがリンクを見失うリスクがほぼなくなります。

修正前(ピンクになる状態): プレハブは「フォントアセットの中にあるサブマテリアル」を参照しています。Addressablesなどでロードする際、この「アセットの中のアセット」へのリンクが正しく解決されず、迷子になりやすいです。