Unity UI Toolkit でマップ画面 with RenderTexture
初めに
UI Toolkit でマップ画面を実装しようとしていました。 マップ表示までは直ぐに出来たものの、マップにあるオブジェクトと同じ位置にアイコンを置こうとするとずれる問題が起きました。 解決までかなり時間が掛かりましたが、問題が無くなったと思われるのでまとめます。
- Unity 6000.1.15f1
手順
- RenderTexture を適当な場所に作る
- アセット → 作成 → レンダリング → レンダーテクスチャ
- RenderTexture インスペクタでパラメータ変更
- サイズをスクリーンサイズと同じ大きさにする
- 異なる大きさにするなら縮尺の補正などが必要になる。難しいので一旦完成してからにする。
- サイズをスクリーンサイズと同じ大きさにする
- マップ用のカメラを置く
- カメラのインスペクタでパラメータ変更
- 上空の適当な高さに上げて、真下を向ける
- 投影方法 ー 投影方法 ー 平行投影
- 出力 ー 出力テクスチャ に、作った RenderTexture をセット
- UI Toolkit の設定
- Background ー Scale Mode を2番目の scale-and-crop にする。
- 他のモードは事故の元。一旦完成してからにする。
- Background ー Scale Mode を2番目の scale-and-crop にする。
- コードでレンダーテクスチャーをセット
- 後述のサンプルコードを参照
- アイコンを置く
- 後述のサンプルコードを参照
苦戦した原因など
- 画面上部に広告バナーを配置する場所を開けていた。
- UI Toolkit のパネル内ワールド座標とパネル内ローカル座標が理解出来ていなかった。
- UI Toolkit の背景に関するパラメータの理解不足
- RenderTexture の理解不足
サンプルコード
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);
}
}