Unity UI Toolkit でマップ画面 with RenderTexture

初めに

※ このページは前々回前回を前提にしています。

UI Toolkit でマップ画面を実装しようとしていました。 マップ表示までは直ぐに出来たものの、マップにあるオブジェクトと同じ位置にアイコンを置こうとするとずれる問題が起きました。 解決までかなり時間が掛かりましたが、問題が無くなったと思われるのでまとめます。

手順

  1. RenderTexture を適当な場所に作る
    • アセット → 作成 → レンダリング → レンダーテクスチャ
    • レンダーテクスチャーコマンド
  2. RenderTexture インスペクタでパラメータ変更
    • サイズをスクリーンサイズと同じ大きさにする
      • 異なる大きさにするなら縮尺の補正などが必要になる。難しいので一旦完成してからにする。
    • RenderTextureインスペクタ
  3. マップ用のカメラを置く
  4. カメラのインスペクタでパラメータ変更
    • 上空の適当な高さに上げて、真下を向ける
    • 投影方法 ー 投影方法 ー 平行投影
    • 出力 ー 出力テクスチャ に、作った RenderTexture をセット
    • カメラインスペクタ
  5. UI Toolkit の設定
    • Background ー Scale Mode を2番目の scale-and-crop にする。
      • 他のモードは事故の元。一旦完成してからにする。
    • UIToolkit設定
  6. コードでレンダーテクスチャーをセット
    • 後述のサンプルコードを参照
  7. アイコンを置く
    • 後述のサンプルコードを参照

苦戦した原因など

サンプルコード


using UnityEngine;
using UnityEngine.UIElements;

namespace Assets.Scripts
{
    internal static class Extensions
    {
        public static void SetRenderTexture(this VisualElement ve, RenderTexture renderTexture)
        {
            var bg = Background.FromRenderTexture(renderTexture);
            var backgroundImage = new StyleBackground(bg);

            ve.style.backgroundImage = backgroundImage;
            ve.style.backgroundSize = new StyleBackgroundSize(new BackgroundSize(renderTexture.width, renderTexture.height));
        }

        public static void AdjustBackgroundPosition(this VisualElement ve)
        {
            var delta = ve.PanelLocalToPanelWorld(Vector2.zero);

            ve.style.backgroundPositionX = new(new BackgroundPosition(BackgroundPositionKeyword.Left, -delta.x));
            ve.style.backgroundPositionY = new(new BackgroundPosition(BackgroundPositionKeyword.Top, -delta.y));
        }

        public static void MoveCenterFromPanelLocal(this VisualElement ve, Vector2 panelLocalPos)
        {
            ve.style.left = panelLocalPos.x - ve.resolvedStyle.width * 0.5f;
            ve.style.top = panelLocalPos.y - ve.resolvedStyle.height * 0.5f;
        }

        public static void MoveCenterFromScreenPos(this VisualElement ve, Vector2 screenPos)
        {
            var panelLocalPos = ve.parent.ScreenToPanelLocal(screenPos);
            ve.MoveCenterFromPanelLocal(panelLocalPos);
        }

        public static void MoveCenterFromWorldPos(this VisualElement ve, Camera camera, Vector3 worldPos)
        {
            var panelLocalPos = camera.WorldToPanelLocal(ve.parent, worldPos);
            ve.MoveCenterFromPanelLocal(panelLocalPos);
        }
    }
}

using UnityEngine;
using UnityEngine.UIElements;
using Assets.Scripts;

public class SelectMiniMap : MonoBehaviour
{
    public RenderTexture SelectMinimapRenderTexture;
    public UIDocument UIDocument
    public Camera SelectMinimapCamera;

    VisualElement icon;

    void Start()
    {
        var root = UIDocument.rootVisualElement.Q("panel-root");
        root.SetRenderTexture(SelectMinimapRenderTexture);
        root.AdjustBackgroundPosition();

        // 動的にアイコンを作る場合
        Sprite sprite = LoadSprite();// TODO アイコン読み込み

        icon = new VisualElement()
        {
            name = "object-name",
            style =
            {
                backgroundImage = new StyleBackground(sprite),
            }
        };
        icon.AddToClassList("icon"); //スタイルでアイコンの大きさを指定
        root.Add(icon);// 大量に作るならアイコン用のルートを作る
    }

    void Update()
    {
        var worldPos = new Vector3(0f, 0f, 0f);// TODO オブジェクトのワールド座標
        ve.MoveCenterFromWorldPos(SelectMinimapCamera, worldPos);
    }
}

top

その他の投稿
20250812-02 Unity UI Toolkit でのタップ操作検出
20250811-01 Unity UI Toolkit での座標変換
20250721-01 Unityでカメラが平行投影の場合にScreenToWorldPointがズレる
20250712-01 Unity既存プロジェクトにURP追加
20250320-01 Unity のナビゲーションシステム 追記
20241013-01 Google Play で開発者情報が公開される件
20240824-01 Unity UI Toolkit カスタムコントロールサンプル
20240720-01 Unity NavMesh サンプル
20240529-02 Unity ECS の Android ビルドでエラー
20240529-01 Unity ECS で、Prefab を使用した場合に Android ビルドでエラー