こっこのぶろぐ

kockoにまつわる何かを書いていく

C++でPrimaryAssetのAsyncLoadを実現する方法【UE5】

AssetManagerでPrimaryAssetを読み込む手順を紹介してる記事はまぁまぁある。

しかし、C++での非同期読み込みについて言及してる日本語の記事が少ないと思ったので、備忘録がてら記載しておこうと思う。

前提として、今回の実装は

● DataTableにC++からアクセスする為に、DataTableのインスタンスを管理するためのPrimaryDataAsset(class UDataTableReference)を作っておく。

C++側でUDataTableReferenceの読み込みを行うことで、DataTableのインスタンスC++で能動的に使えるようにする

という流れで実装したものの一部。

BP側からDataTableのインスタンスを直接C++側に渡すことも出来るが、BPとC++のやりとりが増えれば増えるほど処理が追いづらくなるので、今回はあくまでもC++主導でDataTableの管理を行いたい。

また、C++のみでBPで実装されている特定のDataTableを参照する事もできるが、ファイルパスの指定をハードコーディングする必要があったのが気に入らなかったので却下した。

ちなみに、ここで読み込みたいDataTableはレベルに依存したものであり、今回はプレイヤーがゲーム世界を探索する際の各種マスターデータとして実装しているDataTableを読み込んでいるので、読み込み処理はレベル(class ABaseMap)が実行している。

void ABaseMap::LoadDataAsset()
{
    auto& assetManager = UAssetManager::Get();

    TArray<FPrimaryAssetId> assetIdList;

    if (assetManager.GetPrimaryAssetIdList(UDataTableReference::AssetType, assetIdList))
    {
        assetManager.LoadPrimaryAsset(assetIdList[0], { }, FStreamableDelegate::CreateUObject(this, &ABaseMap::OnDataTableReferenceLoaded, 
    assetIdList[0]));
    }
}

● GetPrimaryAssetIdListについて

引数で指定したPrimaryAssetTypeに該当するPrimaryAssetIdを配列で取得できる。

(PrimaryAssetTypeの実態はFNameみたいなものなので、直接TEXT("")を渡すことも可能)

該当したPrimaryAssetIdは、同じく引数で指定されたTArrayに格納されるので、アウトプット用の変数を作っておく。

(ここで言うassetIdList)。

また、該当するPrimaryAssetIdが1つ以上あるかどうかが関数の戻り値でboolとして返ってくるので、ifで制御しておく。


● LoadPrimaryAssetについて

実際にPrimaryAssetの読み込みを行う関数。

読み込みたいPrimaryAssetに対応するPrimaryAssetIdを、引数として”一つだけ”渡せる。

(複数読み込みたい時は、LoadPrimaryAsset"s"を使う)

2番目の引数には、いわゆるAssetBundlesを配列として渡せる。

今回はmeta tagでのAssetBundlesの指定はしていないので空の配列を渡している。

3番目の引数には、ロードが完了した時のコールバックを渡す。

コールバックの引数に渡したいものは、 FStreamableDelegate::CreateUObjectの引数に渡せば良い。

細かい事は調べてないが、多分ラムダ式と似たような感じな気がする。

恐らくキャプチャした変数を渡してるだけだと思うので、ローカル変数を渡したい時は値渡しするのが無難だと思う。

void ABaseMap::OnDataTableReferenceLoaded(FPrimaryAssetId assetId)
{
    if (const auto& dataTableReference = UAssetManager::Get().GetPrimaryAssetObject<UDataTableReference>(assetId))
    {
        ~~
    }
}

これはコールバックで指定した関数の実装。

引数で渡されたPrimaryAssetIdを使い、AssetManager::GetPrimaryAssetObjectを実行することでロード済みのPrimaryAssetを取得出来る。

終わり。


今回、C++でAssetManagerを使う為の参考資料がガチで少なくて困った。

普段なら、一つの機能に関して複数の参考資料を参照して自分の理解が正しいか多方面的に確認していくのだが、PrimaryAssetに関しては有名な資料が2、3個あるだけだったのでそれが難しかった。

というか、複数の資料が1つの資料を参照してる事が多く、情報としては結局一つしか手に入らないという事が多かった。。

この参考資料の少なさは、C++使えるなら自分でエンジンのコード見て理解できるよね?という暗示なのだろうか。

僕は昔取った杵柄でC++もBPも理解できるからギリギリ何とかやれているが、初心者にとってあまりに厳しい環境だなと思う。

PrimaryAssetに限らないが、自分が使いたい機能を調べてみると、有名で色んな人が参考にしてる記事が2、3個出てくるだけで、そこで紹介されている使い方から少しでも外れたことをしようものなら途端に情報不足に陥ってしまう。

まぁ、基本的な使い方が分かれば後は当人の工夫次第でどうにでもなるし、使い方も千差万別なので解説しようがないのかもしれない。。

だとしても、皆がどの機能をどうやって使ってるのかとか、工夫の仕方とかの情報がもっと充実してればいいなと思う。

とりあえず、(Unityはもっと情報に溢れてて楽しかったぞ!)という事だけ心の中で叫んでおきたい。

と思ったので、まだまだ理解力不足だと思うものの、一旦記事にしようと思った次第です。

間違ってることがあれば適宜修正するのでご連絡ください。


(ΦωΦ)フフフ…


参考記事

今回参考にした記事。
unrealcommunity.wiki

↑の記事が参考にしてるリポジトリの該当箇所付近。
github.com


多分、この記事を参考にして一番困るのがMonsterIdというPrimaryAssetIdの存在だと思う。

これはDataTableに登録されているPrimaryAssetIdを使ってるということになると思う。

しかし、そうなるとPrimaryAssetを読み込むためのPrimaryAssetIdを保持しているDataTableを読み込む必要が出てくるので、少なくとも僕はそのまま流用できなかった。

まぁ、PrimaryAssetを使う上で、PrimaryAssetIdないしはPrimaryAssetTypeをどう管理するかが人によって分かれる所なのかなぁと思った。


(ΦωΦ)フフフ…

Unityに逃げようとして色々試してみた結果1日目【Unity2022.3、UE5】

UnrealEngineは素晴らしいエンジンだ。

ゲーム作りに必要な機能はほぼ全て標準搭載されている。

入力検知、物理計算、UI、3Dモデルのボーンやメッシュの調整機能、モーションとアニメーション、ユーザーデータやアセットの管理機能等々。

それらの豊富でリッチな機能を組み合わせていくことでゲームが出来上がっていく。

ユーザーが自前で実装するのは、些細な足りない機能性を補ったり、利便性を向上させる時くらい。

あとは標準搭載されている機能を組み合わせていけば自然とそれらしくなっていくので、とてもモチベーションが上がる。

以前にも記事にしたが、UEは既に出来上がった機能の山を切り崩していくことでゲームを作っていくという指向性のエンジンである。


だが、そんなUEにも弱点がある。

それは、機能が出来上がりすぎているが故に、使いたい機能が気に入らない挙動をした時に修正するのがかなり大変なことだ。

UMGに関しては特にそう思う。

Widgetの挙動が怪しい所があったり、表示に対して影響が大きいパラメーターがエディター上に公開されておらずC++を使わないといけなかったりする。

直近で遭遇した問題は、ScaleBoxを階層に含めたWidgetの表示・非表示を切り替えるとスケーリングする瞬間が画面に映ってしまいチラついて見えることとか。

ListViewのスクロールバーの挙動に関する制御項目が、エディター上に殆ど無いこととか。

C++が使えるというのは明らかなメリットなのだが、製品としてリリースすることを考えると結局C++は必須だ。

また、UMGに関しては表示の更新も寿命の管理も全てエンジンが行っていて、にわかに手が出せる範疇ではないのも苦しくなる要因の一つだ。

まぁ当然といえば当然ではある。

素人が勝手に触り始めてグラフィックのライフサイクルを破壊するオチが容易に見える。

ならUEが全てやってくれる方が優しいまである。

しかし、だからこそ意図しない挙動をされた時の解決がとても難しくなってしまうのもまた事実。


うーん。

まぁどう考えてもUEが良いツールであることは間違いない。

間違いないんだけど、ストレスが溜まる所に遭遇すると滅茶苦茶ストレスが溜まるという感じ。

言ってしまえば、ゲームシステムを作るには最高の環境なのだが、UMGをいじり始めるとめっちゃダルい。

っていうかUMGでしかストレス溜まってない。

だから、「高機能じゃなくていいから全体的に不具合や謎挙動が少なく、問題が起きても手元で解決しやすい環境がいい」ということで、一旦UEのプロジェクトは休止してUnityをインストールしたのだった。

いやUnityも相当高機能ではあるけど。


以前、Unityでゲームを作っていたことはある。

なので、勝手知ったる我が家に帰ってきたようなものだ。

と言っても、本格的に使ってたのはUnity3の頃だったので記憶の彼方である。

だからこそ、Unity2022を触った時に驚いたのが、使い勝手が殆ど変わってないことだ。

それでいて機能は充実してるし、操作性も洗練されている。

Unityの一番好きな所として、機能が適材適所に配置されており、無駄が少ないので直感的に操作できるところだ。

デフォルトの状態だとゲームを作るには物足りなさがあるが、足りない機能に関してはAssetStoreを駆使する前提のエンジンである事がよくわかる。

つまり、UEが機能を切り崩す事でゲームを作るツールであるのに対して、Unityは自分で好みの機能を盛っていくツールなのだ。


さて、ここからが本題だ。

Unityを使い始めて真っ先に確認した機能が、ユーザーの入力を受け取るためのInputに関する機能だ。

何故なら、UEではEnhancedInputという仕組みがあるのだが、これが個人的に結構気に入ってる機能だからである。

見やすくて使いやすいし、適度な粒度で設定用のファイルが分割されてるので管理がとても楽だった。

設定項目も不足が無く、とても好印象だ。

Unityにおいては、InputSystemがその役割を果たすことになる。

のだが、必要十分な機能性ではあるが、正直UEのEnhancedInputに比べると扱いづらいなと思った。

今回は、動かしたいキャラクターオブジェクトにPlayerInputをアタッチしてInputActionAssetを作り設定した。

しかし、どうにもInputActionAssetの設定項目が何を言ってるのか意味が分からなかった。

Schemeが何なのかとか、ActionMapsって何?とか。

(Up¥Down¥Right¥Left Compositeってなんだよ。。)とか。

ただ、こうなっている理由はUnity初心者ながらにちょっと分かった気がする。

Unityは、設定項目にカーソルを合わせると説明用のToolTipが表示されて、そこで項目の説明が全てなされている。

他にも、インスペクターに長々と説明用のテキストが表示されていたり。

恐らく、「説明があるから設定項目が多少煩雑だったり難解でも大丈夫」という前提で作られてるのだと思う。

その点UEは、設定可能項目が並ぶ中に説明用のテキストが場所を専有してることはない。

甘え、なんて表現をしたらツールの世話になってる分際であまりにも失礼な物言いだとは承知の上で、エディターのデザインが弱いのにはそういう甘えが少なからず含まれてる印象を受ける。

ToolTipでの機能の説明については、UEは逆に説明が足りなさすぎると思うときもあるが。。

そういった、エンジンを作る上での思想の違いのようなものを感じつつ、どっちが良いというわけでもないがそういう違いはあるなと思った。

また、入力を検知した時のイベントの作られ方にも違いを感じる。

というのは、UnityのInputSystemは『入力の状態が変更された時にのみ入力イベントをユーザーに通知する』のに対して、UEは『入力を検知する条件を満たす限りイベントを通知し続ける』という作りになっている。

最初は戸惑ったが、恐らく関数呼び出しのコストを考えてのことなのだろうと思うので、ここは一長一短だとも思う。


まだUnityに復帰して1日なのでそれほど多くの事は言えないが、総合的に考えて『Unityは機能性に不足は無いが物足りなさもありつつ、機能作りは基本的にユーザーに任せられているのでエンジン自体は安定している』という感じ。

どこまでいってもAssetStoreの存在が強力すぎるので、それだけでUnityを使う価値はあると思う。


いずれにせよ、自分が作りたいものの為にどんな機能が必要でそれを実現してくれるアセットがあるのかどうか、無いならどう作るかというのを考える必要がある。

なので、UEと比べて方向性が違えど結局労力は大して変わらない。

求められる知識や技術の形や質が違うだけで、量は同じだと思う。

よって、どちらが良いという記事ではない。

あくまでも、Unityに鞍替えしてみて思った事を書いただけ。

一つ言えるのは、製品として一本ゲームを出してみたいという開発初心者には、個人的にはUEを勧めるかもしれない。

何度も言うが、結局はそれなりの知識と技術、そして苦労が求められる。

だから、どっちのエンジンを使ってもその水準を満たせるだけの努力を出来ない人はいずれにせよゲームは作れないと思う。

ただし、初心者が期間を設けてゲームを一本作ってみる事に挑戦した時、出来上がるもののクオリティはUEの方が高いかもと漠然と思う。

それは、UEが元から機能が盛り盛りにされてるからというのが大きく、何をどう実装したら良いのか迷子になりづらいという個人的なイメージによるものである。

Unityは、自由だが、まるで何も無い平原を歩いてるようなものだ。

そこにどんな家を建てて、木を植えて、庭を作り、動物を放すかは全てユーザー次第。

それはUnityの面白さでもある。

まぁ、好きな方を選べば良い。

最新版の2つのエンジンに触れてみて、率直に思ったことを書いた日記でした。

(ΦωΦ)


PS.

そういえば、当たり前過ぎて言及し忘れてたけど、UnityはC#で開発が1本化されてるのも重大なメリットの一つだよね。

Unity自体はそこまで難しい事をしてるツールではないので、C#さえ分かってれば取り付きやすいっていうのは大きい。

また、コードベースであるというのはUEを使ってると特にありがたく感じるところでもある。

UEのBPは強力な機能ではあるものの、バイナリデータというだけでバージョン管理や問題発生時の対処の難易度が格段に上がってしまう。

そういう意味を踏まえてUnityの安定性を個人的に高く評価していきたいと思っている。

C#でしか開発出来ないのではなく、C#で開発出来るというメリットはあまりにも大きい。


(ΦωΦ)

全画面でWidgetを表示した時にカメラが動く件について【UE5】

UIを全画面で表示している際に、マウスの入力がUIの奥にあるゲーム空間まですり抜けないよう入力を阻止するUIを、一般的にモーダルと呼んだりする。

大抵は、全画面で表示しているUIの背景画像がその役割を果たすことが多い。

なので、全画面のUIを表示してる時にマウスの入力を行った場合、入力によって起きる振る舞いは全てUIで実装されてる処理になる。

UIよりも奥に存在するゲーム空間に、マウスの入力が影響を及ぼすことはない。


例えば、UMGでImageを画面いっぱいに表示しShowMouseCursorをtrueにすることで、背景に存在しているゲーム空間にマウスの入力が通り抜けることが無くなる。

はずなのだが、左クリックしてマウスを動かすとカメラが動いてしまう問題に当たった。

何故左クリックしながらマウスを動かすとカメラが動くのか。

そして、何故左クリックしながらならカメラを動かせてしまうように実装されているのかは謎だ。


まぁそこは一旦置いておくとして、解決策を調べると色々と参考記事が出てくると思う。

主に、SetInputModeUIOnlyだ。

ただ、これは運用上の問題点がいくつか指摘されている。

その多くは、SetInputModeUIOnlyを実行した際に入力のハンドルが強制的にUIに限定されてしまう仕様によるものだ。

よくあるのが、キャラクターの移動入力中にSetInputModeUIOnlyを実行すると、移動キーを離したという入力が通知されないので、UIが表示されている間延々とキャラクターが歩き続けてしまうというものだ。

これについては、SetInputModeUIOnlyの引数であるFlushInputという引数にチェックを入れれば解決は出来る。

ただし、これはあくまでも内部的に「DownされているキーがあればRelease入力を与える」というだけに過ぎず、強制的にReleaseした扱いにしてるだけに過ぎない。

恐らく、大抵の場合これで問題無いと思われるが、感覚的にはReleaseした事にするのではなく処理として入力がキャンセルされたことにしてほしくはある。

なんというか、入力してないものを入力したことにするというのが、その場しのぎ感があって好きになれない。

ということで、そういう変なこだわりを持ってる人に向けて、解決策の一つを提案したいと思う。

それが、GameControllerが持っているSetIgnoreLookInputだ。

簡単に言うと、指定のGameControllerが所有権を持っているカメラの回転について、マウスの入力を無視するための機能である。

そもそもの話、GameControllerがカメラを回転させる際には常にIgnoreLookInputという変数が参照されており、trueの場合カメラを回転させないように実装されている。

SetIgnoreLookInputでは、その変数を必要に応じて更新することが出来る。

なので、UIを表示している際にカメラを動かさないという要求に対し、SetIgnoreLookInputを用いることで限りなく最小限な影響で解決させることが可能だ。


また、SetInputModeUIOnlyだとキー入力のイベントなども一律で無効化してしまうらしい。

即ち、例えばTabキーでUIを表示/非表示切り替えたいとしても、SetInputModeUIOnlyを実行するとTabキーの入力アクションが無効化されてしまうのでトグル出来なくなるというのもあるようだ。

僕はSetInputModeUIOnlyを使わないので詳細な挙動は不明だが、そういう運用上の問題を報告している記事もある。

強いて言えば、僕の作るゲームは、UIを表示していてもキャラクターが移動可能な状態でキープしておきたい。

なので、入力アクションを無効化されるとそれが叶わなくなるのでいずれにせよ採用の余地は無い。

という感じで、UIを表示してる時にカメラが動いちゃうよぉ!ふえぇ!って人は、カメラの動きを止めるためだけにわざわざSetInputModeUIOnlyという大仰な機能を使うのではなく、SetIgnoreLookInputを使用することをおすすめしたい。


(ΦωΦ)

ToolTipで表示するWidgetの表示がチラつく問題について【UE5】

UEのWidgetにはToolTipという機能が備わっており、有効化すると、そのWidgetにマウスカーソルを合わせた時に概要説明用の小さなUIを表示することが出来る。

これは、ListViewに表示しているEntryのWidgetのToolTipを有効化し、マウスカーソルを合わせた状態のスクリーンショット

(マウスカーソルが消えてるけど、ListViewの左下のEntryにカーソルを合わせていて、そこを起点として右下方向にToolTipが展開され表示されている状態)

ゲームのアイテムインベントリに雑多に存在するアイテムについて一つ一つ丁寧に説明用のUIを表示するのもいいが、マウスカーソルを合わせた時に概略を説明するための小さなUIを用意するのもまたいい。

例でいうと、テラリアやFF14のアイテムインベントリは基本的にToolTipによってアイテムの説明をしている。

まぁ、UIの在り方に関してはUX無くしては語れないものであり適材適所ではあるが、ToolTipは選択肢の一つとして有力である。

さて、このToolTipに設定するWidgetは、自分でWidgetBluePrintを作成し、あくまでもWidgetとして実装する必要がある。

細かい実装方法はToolTip自体の解説になってしまうので割愛する。

今回起きた問題は、ToolTipを設定したいくつかのUIの上でマウスカーソルを行ったり来たりすると、UIの描画更新が追いつかずにチラついてしまうというもの。

前提として、僕はWidgetを作成する時にほぼ必ずと言っていいほどルートにScaleBoxを親として設定する。

これは、そのWidgetを使う側の方でサイズ指定した際に、UIが崩れないようにするための施策である。

しかし、この設計には一つ問題があり、子として設定しているWidgetに表示するコンテキストが変更され、子階層でWidgetのサイズ変更が行われると、ScaleBoxによる子Widgetのスケーリングとの兼ね合いが悪いのか、Widgetがチラついてしまうのだ。

例えばScaleBox - Border - Canvas - TextBlock, TextBlockとう感じで、最下層に配置されているTextBlockのサイズによって最終的なWidgetとしてのサイズが確定する場合、TextBlockに設定した文字の内容が変更されてTextBlockのサイズが動的に変更された時、今回の問題が起きる。

なんでだろうね。

チラつく様を見せたいのは山々だが、動画を撮るほどこの記事を書くのに手間をかける気もないしただの備忘録なので気になる人は自分で試してみてください。

ScaleBoxを挟んだ時、Widgetを生成して表示状態になった後にスケーリングがかかるような挙動をしていて、一瞬元のサイズで描画されてから拡大されるのでチラついて見えてしまう。

よって、ToolTipに設定するWidgetではScaleBoxを使わずにWidgetのサイズをコントロールしたほうが良いと思われる。

実際、ToolTipに限らず、描画しているWidgetのコンテキストが頻繁に変更されるような機能の場合、このスケーリングが問題になるかもしれない。

なので、自分が初めて遭遇したケースがToolTipだっただけで、同じようにWidgetがチラつく問題に当たってる人はScaleBoxを用いずに実装することを試してみてほしい。


(ΦωΦ)

文字列から記号を削除したかった者のFString【UE5】

以前、正規表現を用いて文字の抽出を行う必要に駆られ、実装しようとFRegexMatcherFRegexPatternを試した事がある。

だけど、空の文字が返ってきたり、正規表現にそぐわない文字列に整形されて返ってきたりと、どうにも釈然としない挙動をしてたので諦めた記憶がある。

以来、文字列の操作弱いのかなぁなんて考えていたけど、今回もそんな印象を受ける出来事があった。

というのも、EditableTextBlockに入力された数値にカンマが入ってしまうので、カンマを取り除いた状態の文字列が欲しかった。

つまり、指定の文字列の中から特定の文字を削除するという機能がFStringに欲しかったんだけど、無いんだね・・・?

調べてみても、検索と分割に関する機能は用意されてるけど削除するものは無さそう。

まぁ分割できれば実質削除も出来るよね、ということなんでしょうか。

標準のC++なら、eraseとremoveを組み合わせて一文で出来た気がするけど、UEではどうなんしょ。

今回はデバッグ用の機能に使う予定なので、負荷に関してはそこまでシビアに考えてないのもあり、とりあえず用意されてる機能で順当に出来そうな実装をしてみました。

FString UStringHelper::RemoveString(const FString& SourceString, const FString& RemoveString)
{
	TArray<FString> outStringArray;

	const auto& parsedArray = SourceString.ParseIntoArray(outStringArray, *RemoveString);

	FString retString;

	for (const auto& string : outStringArray)
	{
		retString += string;
	}

	return retString;
}


(ΦωΦ)

なんかアレだよな【UE5】

UEでC++使ってると、特にファイル名に困る。

例えば、Widgetクラスと、そのWidgetに表示するためのデータを保持するためのクラスを作るとする。

通常のC++ならば、Widgetクラスを宣言した後にそのインナークラスとしてデータクラスを宣言したり、名前空間で区切ったりすればいい。

そうすることで、Widgetの利用者に対して互いの強力な依存関係を表現しつつ、関連機能の所在を明確にすることが出来る。

普通はそういう感じでクラスの名前を節約したりするが、UE上ではそういうのが全くと言っていいほど出来ない。

UEC++環境下では、WidgetクラスをWidgetという名前にした上で、Widgetのデータを保持するクラスはWidgetDataという名前で別クラスとして宣言する事になる。

更に言えば、仮にListViewでDataTableの中身を全て表示する為のWidgetを作る場合、DataTableListViewというWidgetクラスの名前になる。

そして、そのListViewのEntryはDataTableListViewEntryという名前になるし、そのデータクラスはDataTableListViewEntryDataという名前になる。

これはあくまでも例として普遍的な命名をしてるからまだしも、実際のプロジェクトではもっと具体的な命名になると思う。

インナークラスも名前空間も使えない上に、C++のファイル名が32文字以内という制約のあるUEC++環境下で、取り違いを起こさない程度に具体的な名前をファイルの名前にするのがとてもキツイ。

強いて言うなら、デバッグ用の機能にもプレフィックスで”Debug”みたいに、デバッグ用の機能であることを表す文字を入れないといけない。

普通、デバッグ用の機能が必要ない実装箇所についてはファイルをインクルードしなければいいだけだし、名前空間をインポートしなければいいだけだが、UEではそうもいかない。

なんか良い方法あるのかなぁー。

同じ問題を抱えてるプロジェクト絶対たくさんあると思うけどなぁー。

ということで、どう解決するか。

仮にDataTableの中身を全て表示する為のListViewを作る場合、ファイル名は汎用的に”DataTableListView.h”みたいなファイル名にしておいて、似た用途のクラスは全て同じファイルに宣言するのが良さそうと思った。

DataTableであるAとBがある場合、DataTableListView.hにAListViewとBListViewを2つとも宣言する。

データクラスに関しても同じ。

本来、1ファイルのボリュームは必要最小限に抑えるために1ファイル1クラスが基本だと思うが、今回ばかりは無理だ。


(ΦωΦ)

PaddingでWidgetの配置を整形すると表示がおかしくなる件について【UE5】

HorizontalBoxでTextBlockを並べていたのだけど、Paddingで間隔を調整すると後続のUIにTextBlockが食い込んで表示がおかしい。

以下の画像は、左からImage、TextBlock①、TextBlock②という順番で、HorizontalBoxの子としてWidgetを配置している。

TextBlock①のPaddingのLeftに数値を入れると右側に指定の数値分だけ座標が移動するのだが、TextBlock②に食い込んでしまう。

なので、ImageとTextBlock①の間にSizeBoxを挟み、座標を移動させたい分だけSizeBoxのサイズ設定で空間を占領して実質Paddingとすることにした。

ちなみに、おかしいと言ってもUEの仕様的な話ではなく、こちらが期待した表示にならないという意味。

Paddingで位置を調整するとバグるという意味ではない。

まぁWidgetが食い込むのがバグのせいなのかどうかは知らないが、なにはともあれ期待した表示にはならない場合はこういう解決策もある。

少なくともSizeBoxの用途と違う気がするので違和感が強いが、調べると似たような整形の仕方をしてるサイトも目につくので、UE使いの間ではこういうのもまた有り得る手法なのだろう。


(ΦωΦ)