みつまめ杏仁

アンリアルエンジン(UE4)でGUIを作るためにゴニョゴニョしてます。UIデザイナーの皆様の助けになれば幸いです。

3Dオブジェクトの上にWidgetをマーカーのように表示できたけどどうなってるの?なんか計算がややこしいのだけれど。

という長いタイトルを付けるのが最近のトレンドぢゃ、とじっちゃが言ってたのでさっそく実践してみたけど、なんというか全然キャッチーにならないのはセンスがないから?などと架空の祖父まで召喚してくだらない言い訳をつらつらと書きながら花粉の猛襲に苛まれる目をひたすら擦りながらキータイプもままならない状態でややこしい記事を書こうとしている折、皆様ますますご健勝のことと存じます。

さてさて前回はとりあえず動くところまでのチュートリアル的な記事を書きました。

少ない脳みそを駆使して解説をしてみようと思います。実際はこうやって書きながら自分の頭を整理してログを残すのが目的だったりします。

 

 さてさて

今回の処理で扱う重要な数値は以下の5つ

  • 描画のための画面サイズ  → Get Viewport Size
  • 追随するWidgetのサイズ  → Get Desired Size
  • 表示スケール  → Get Viewport Scale
  • アクターの表示位置  → ConvertWorldLocationToScreenLocation
  • 最終表示位置Set Position

 

 これらの値があれば計算できるのですが、ひとつ厄介な問題があります。それは最終的な表示サイズが必ずしも画面の大きさと同じとは限らないという問題です。

 たとえば開発中のエディタ解像度を 1920x1080 で進めたとしても、実際遊ばれる環境は、1080pじゃない場合が普通にありえます。フルスクリーンにしろウィンドウにしろユーザーに任されていて遊ぶ環境によって変わるため、UE4では表示する際に、表示されている解像度に応じて一定量のスケール値を掛けることで、元のレイアウトが崩れないように描画してくれます。

 また表示する際に指定する座標は、描画のための画面サイズであるエディタ解像度に準拠するので、スケールを掛けないように指定する必要があります。

 スケールが掛かっていたり掛かっていなかったりする値をうまく整理しながら計算しないといけないのです。

 

開発環境にてエディタ解像度を 1920x1080 にして、画面には 1280x720で表示している場合、上にあげた5つのノードで扱う値は、以下のようになっています。

 

f:id:hiyokosabrey:20190310012023p:plain

Get Viewpor Size は表示されている解像度です。(基本的に可変します)

Get Desired Size にはキャンバスに置いたときの値で、スケールが掛かっていません。

 

f:id:hiyokosabrey:20190310010520p:plain

 

f:id:hiyokosabrey:20190310012429p:plain

 

ここまではGET。最後にSET。

 

f:id:hiyokosabrey:20190310013029p:plain

 

このようになっているので、@ghosticgames さんの手順をみてみると、基本的に スケールの掛かった ViewportSize ベースで計算して、表示位置が確定した後に ViewportScale で割り算して、1920x1080空間用に合うようにしています。

f:id:hiyokosabrey:20190310014617p:plain

 

Get Desired Sizeで得た追随用パーツのサイズに ViewportScaleを掛けているのは、スケールを揃えないとこの後の計算がおかしくなるからです。

f:id:hiyokosabrey:20190310014943p:plain

 

ではこの辺で Clampさんのお仕事紹介と行きましょう。

Clampは3つの入力ピンがあります。

f:id:hiyokosabrey:20190310125803p:plain

Value から入った値が、Min と Max の範囲を越えても、Min(下限) と Max(上限) の範囲から出ないことを保証してくれるという代物。

下図はイメージ。

 

f:id:hiyokosabrey:20190310131747p:plain

例えば、Max に 1.0 が設定されている時に、 Value が 2.5 だとすると、強制的に 1.0 になります。

 

 この仕組みを使って、表示座標に制限を付けるのです。

 

 

f:id:hiyokosabrey:20190310133059p:plain

 アクターの表示位置を3D→2Dに変換する ConvertWorldLocationToScreenLocationノードは、スクリーン座標の限界を超えた値も返してくるので、Clampノードにスクリーン座標の最小値、最大値を設定してやります。

 このとき、追随するWidgetがはみ出さないないようもしたいので、そのぶん内側に制限を付けることになります。

 

f:id:hiyokosabrey:20190310145306p:plain

 

ViewportSize から 追随するWidgetの表示サイズの半分のサイズを内側に入れればよいということになります。改めてノードを見てみるとこうなってました。

f:id:hiyokosabrey:20190310145815p:plain

Local Desired Screen Location はアクターのスクリーン座標です。Viewportの左端は 0 から始まるので、 Min は 追随するWidgetの半分のサイズになります。Max はViewportサイズから 追随するWidgetの半分を引き算すれば求まります。

 

これで、画面内から絶対はみ出さない仕組みが実現できます。

 

 

ということで、ここからは、オマケの仕組みを組み込んでいきます。

f:id:hiyokosabrey:20190310154211p:plain

グラフを開いて、空いているところに、表示する値を受け取るためのカスタムイベントを準備します。

f:id:hiyokosabrey:20190310193804p:plain

カスタムイベントは Float型の ピンを追加します。

f:id:hiyokosabrey:20190310194218p:plain

 

数値に単位の文字「 m 」をくっつけたいので、FormatTextノードを使います。

このノードは、定型のテキストに変数を差し込むことが出る便利なノードです。

{ }でピン名を囲って使います。上の例では、{0}m と入力しています。⑤

 

過去記事にもう少し詳しく書いています。

limesode.hatenablog.com

 

UE4の単位は、Project Settings > Editor > Appearance で設定されています。 

f:id:hiyokosabrey:20190310194925p:plain

 デフォルトでは cm(センチメートル)に設定されています。

 今回 単位を m(メートル)にしたいので、100で割ります。⑥

 割り算の結果と FormatTextノードを直接つなぐことが可能なのですが、小数点の桁数を変更したいので、 ToText (Float)ノード を利用します。⑦

この ToTextノードは詳細設定ができます。

f:id:hiyokosabrey:20190310200846p:plain

Minimum Fractional Digits を 1 にすると、キリのいい値のときでも、0 を表示するようになります。

 

これでWidgetの編集は完了です。

コンパイルして閉じます。

 

次は、このWidgetに距離を調べて渡す部分。

レベルブループリントを編集します。

すでにつないである部分2つ目の Create Widget のReturn Value ピンで右クリックします。

f:id:hiyokosabrey:20190310205114p:plain

Remote To Variable (変数へ昇格)を選択して、変数化します。

f:id:hiyokosabrey:20190310205410p:plain

これに名前を付けて、間に挟み込みます。

f:id:hiyokosabrey:20190310205521p:plain

この操作で、追随するWidgetに対してアクセスが可能になります。

 

これをEventTickで使用します。

f:id:hiyokosabrey:20190310210459p:plain

対象の3Dオブジェクトと、プレイヤーカメラの距離を表示してみます。

双方のポジションを取得して引き算すると差分が得られます。それを VectorLength ノードが距離として返してくれるので、これを利用します。

 

 最初3次元上の2点の距離を調べる公式を使ってノードを組んでいたのですが、

f:id:hiyokosabrey:20190310212922p:plain

f:id:hiyokosabrey:20190310212023p:plain

ブループリントで数式を組むとなんだか不思議な感じがするのは気のせい?

 

検証している途中でステキな記事を見つけたので、シンプルな VectorLength を使った形に変えました。

qiita.com

 

 これで完成です。

 

 再生して確認してみると、

f:id:hiyokosabrey:20190310211607g:plain

ここまで来たらあとは見た目をゴニョゴニョするだけですね。

 

 

いかがだったでしょうか?

実際に組み込もうとすると、もっと機能性や汎用性を持たせた方がいいと思いますが、ゲームメカニクス次第なところもあってこれ以上は踏み込むのは難しいかな。

とはいえ結構個人的に面白いネタだと思うので、もう少し遊んでみようと考えています。

分かりにくいとことかあれば↓コメントかツイッターで質問OKです。

 

こんこんと泉のごとく沸き続ける鼻水と目のかゆみと闘いながら変なテンションで書いています。

いちいち眼鏡外さなくてもさりげなく点眼する方法って開発されないかな。

 

ではでは

ステキなマーカーライフを!

3Dオブジェクトの上にWidgetをマーカーのように表示する。画面外にいっても大丈夫。操作手順もできるだけ丁寧に書いてみたけどどうかな?

前回の更新からちょっと間が空きましたが、ぷちコン作品を作ってました。

で、先日とあるツイートに目がとまったのです。

 

 

 これは、Worldに存在する Actor の上に Widget を表示するという内容で、さらに対象の Actor が画面外に出ても、画面の端に張り付くようにするという 素晴らしいものです。

 3Dオブジェクトの上にWidgetを表示するという方法は、すでにブログ等で公開されているので、目新しさは無いですが、画面の外に出ないようにする工夫に心惹かれました。

  ツイ主の @ghosticgames さんにブログで説明してもよいかコメントで確認したら、"Sure! "(もちろん!)という返事を頂けたので、さっそく当ブログで説明させていただくことにしたわけです。質問箱からもブログのネタにどうかという提案(いい感じに解釈)もあってこれはもうやってやるしかないなと。

 

 

まずは、さっそくツイート画像を見ながら全く同じように試してみました。

その結果がこちら。

 (このツイート、「メディア」にはログとして残ってるけど「ツイート」には出てこなくなった・・・)

 

 基本的な原理はとてもシンプルです。さっそく他の方法はないかとアレコレ実験してみたんだけど、扱い方次第になると思うので、今回は手を加えずに教材として説明してみます。

 

 ツイートされてる画像とレイアウトや変数名などが若干違うものがありますが、基本のロジックは同じ構造です。基本的な動作を作った後でアレンジとして対象までの距離を表示するとこまでやろうと思います。

f:id:hiyokosabrey:20190306231002j:plain

今回はUMGやブループリント編集に慣れていない方でも作れるように操作方法をなるべく細かく書くようにしてみました。(作業の能率がいいので英語環境をオススメします)

 

 

 

用意するWidgetは2つ。

f:id:hiyokosabrey:20190306231618p:plain

 まずは1つ目のWidgetから

 

キャンバスだけのWidget

コンテンツブラウザから、 右クリック > User InterfaceWidget Blueprint

で作成できます。

 

できたらダブルクリックしてエディタを開きます。

エディタウィンドウ左にある、Hierarchy タブの中を確認すると、あらかじめ Canvas Panel_0 というのが置かれているので、クリックして選択。

f:id:hiyokosabrey:20190306233841p:plain


続いて右上の Is Variable というチェックボックスにチェックを付けます。

f:id:hiyokosabrey:20190306233045p:plain

付けることで、ブループリントからこの CanvasPanel_0 に対して直接いじることができるようになります。キャンバスパネル は UIパーツを描画するため使われます。

 

念のためキャンバスの描画サイズを確認。

f:id:hiyokosabrey:20190306234311p:plain

これはこのWidgetを、画面いっぱいまで使って描くよ、という意味合いです。

 

このWidgetブループリントはこれで完成です。

コンパイルして保存したら閉じてもOK.

f:id:hiyokosabrey:20190307002412p:plain

 

 

 

次は2つ目のWidget

 

Actorをフォロー(追随)するWidget

今度はキャンバスのサイズを指定します。

f:id:hiyokosabrey:20190307003540p:plain

Fill Screen と書かれている部分は、プルダウンメニューになっているので、Custom を選択します。

幅(Width)と高さ(Height)を指定できるようになるので表示したい適当なサイズ(単位はピクセル)を入力します。今回は100x100で作りました。

f:id:hiyokosabrey:20190307003734p:plain

 

次にキャンバスに、2つのパーツ Image と TextBlock を配置します。まずは Hierarchyパネル で ドラッグ&ドロップします。

f:id:hiyokosabrey:20190307004520p:plain

ドロップしたら、エディタウィンドウ右のDetails パネルで設定をいじっていきます。

 

 Image の方から。

Anchor(アンカー)はそのままでOK。

f:id:hiyokosabrey:20190307223852p:plain

キャンバスの中めいっぱいになるように、Sizeをキャンバスの大きさと同じ値を入力します。

あとは Detailsタブの Appearance > Tint で好きなカラーをセットしたら下敷きは完成。

 

次にTextBlock。

こちらもAnchorを設定します。

f:id:hiyokosabrey:20190307221700p:plain

中央揃えにしたいので、『Top-Center(勝手に命名)』 を選択。

パラメータは以下。

f:id:hiyokosabrey:20190307222529p:plain

テキストのカラーと文字サイズを決めて、レイアウトをセンタリングにします。

f:id:hiyokosabrey:20190307222916p:plain

あと、これもブループリントから内容を書き換えるので、Is Variable にチェックを付けておきます。

 

キャンバスがこんな感じになればOK。

f:id:hiyokosabrey:20190307222732p:plain

 

いよいよブループリントを編集します。

編集モードを切り替えます。

f:id:hiyokosabrey:20190307224355p:plain

 

切り替わったら、グラフの何もない所で、右クリックします。

f:id:hiyokosabrey:20190307224712p:plain

Add Custom Event を探します。Search のところで検索ワードを入力するか、一番上にAdd Event というカテゴリを開くと見つかります。(←英語環境)

見つけて選択するとノードが現れるので、

f:id:hiyokosabrey:20190307224914p:plain

InitFancyFollowingWidget と名付けておきます。

ここにパラメータを受け取るための 入力ピン を追加します。

このノードを選択したまま(オレンジの枠線をつけた状態)Detailsタブを見てみるとInputs という項目があるので、右にある + ボタンを 静かに2回クリック。

f:id:hiyokosabrey:20190307225733p:plain

適当な型の設定項目が2つ並ぶので、上からプルダウンを開けて、

Actor を探します。

f:id:hiyokosabrey:20190307230610p:plain

2つ目は

Canvas Panel Slotを探して選択します。

f:id:hiyokosabrey:20190307230832p:plain

下のようになればOK。

f:id:hiyokosabrey:20190307225945p:plain

PINの目的(役割)を後から思い出せるように、それぞれに名前を付けておきます。

これでグラフに置いたノードが変化します。

f:id:hiyokosabrey:20190307231151p:plain

次は変数を用意します。この青いピンからドラッグして作成します。

f:id:hiyokosabrey:20190307232229p:plainPromote to variable(変数へ昇格)を選ぶと、ピンと同じ型の変数が、SETノードの形で現れます。

f:id:hiyokosabrey:20190307232352p:plain

名前を付け直したら、2つ目のピンも同じようにして変数化します。

f:id:hiyokosabrey:20190307232719p:plain

これで、ターゲットとなる Actor と配置用の キャンバスパネルスロット にアクセスするための準備ができました。まだこの時点では中身はなくて空っぽな状態です。型が決まった器があるだけです。

変数化できたので、エディタウィンドウ左にある、Variables のリストに追加されているのが確認できます。そこから、グラフにノードとして取り出します。

f:id:hiyokosabrey:20190307235600p:plain

Ctrlキーを押しながらドロップすると、いきなり GET のカタチで取り出せます。

ちなみに Altキーだと、SETのカタチで取り出せます。

 

このノードの中身が入っているか確認してから処理するので、確認用のノード is Valid をつなぎます。

f:id:hiyokosabrey:20190308000317p:plain

なんらかの不具合や処理順などで受け取りを失敗することがあります。その時は中身が無効になることがあるので、このノードでチェックするのが安全です。今回は 両方が有効の場合のみ 処理したいのでAND(Boorean)を使います。これは論理式というやつで『 A かつ B 』という意味になります。両方のIsValid ノードの結果が true になってようやく true になります。

これらを、グラフに最初から置かれている赤いイベントノード、Event Tick ノードにつなぎます。

f:id:hiyokosabrey:20190308000109p:plain

 

右端は、表示位置をセットするノードです。変数CanvasSlotをもう一つGETで取り出すと、そこから安全に取り出せます。

f:id:hiyokosabrey:20190308001627p:plain

f:id:hiyokosabrey:20190308001745p:plain

仕上げに、ポジションを計算する関数を用意します。

今回の要の部分です。

 

座標計算用の関数

MyBlueprintタブの Functions のところにある +ボタンをクリックして新しい関数を準備していきます。

f:id:hiyokosabrey:20190308002405p:plain

 名前を ProjectWorldToScreenClamed としておきます。

f:id:hiyokosabrey:20190308231337p:plain

グラフが切り替わっているので、ノードをつないでいきます。ちょっと長めです。

 

まず、①Get Player Controller 、そのReturn Value からは、②Get Viewport Size

f:id:hiyokosabrey:20190308232618p:plain

 右にある2つの変数は ローカル変数です。この関数内だけという限定された範囲で使うためのものです。関数の外では利用できない変数です。別の見方をするとこの関数以外での利用価値が無い、とも言えます。今回一時的な計算のためだけに存在するのでローカル変数を使用します。

 作り方は、Get Viewport Size ノードの SizeX と SizeY のピンからドラッグ&ドロップするとポップアップメニューが出るので Promote to Local variable を選択すると簡単です。

f:id:hiyokosabrey:20190308233407p:plain

  エディタ左の MyBlueprintタブに、関数を編集している時にだけ現れる Local Variables であらかじめ用意しておくこともできます。

f:id:hiyokosabrey:20190308233822p:plain

ドラッグして作る方が、VariableType(変数の型)が確実に設定されるのでオススメ。

 

まず Viewport の大きさ(画面表示サイズ)を変数に入れておきます。

 

 

今度はこのWidgetのキャンバスに置いたパーツのサイズを取得して、これもローカル変数に取り置きします。

f:id:hiyokosabrey:20190309105044p:plain

 

まず①Get a reference to self ノードから始めます。

f:id:hiyokosabrey:20190309001902p:plain

Get Desired Size表示しようとしているサイズを返します。Self(自身)に対してつないでいるので、キャンバスに配置したすべてのパーツを含めた最終的な表示サイズのことを指します。

③は vector2d と Float を掛け算するノードです。ドラッグ&ドロップして  *アスタリスク)で検索すると見つかります。

f:id:hiyokosabrey:20190309002428p:plain

 

④Get Viewport Scale は、ディスプレイ解像度を 1 として、UE4実行時に実際に表示されているウィンドウの表示倍率を返してくれます。

f:id:hiyokosabrey:20190309105223p:plain

ディスプレイの解像度設定は公式ドキュメントにあるように設定ファイル "GameUserSettings.ini" に書かれています。

 

⑥は 複数の型で構成されているVector系のデータを分解してくれるノードです。Vector2D 型 は Float型のデータを2つ束ねています。主に XY座標を扱う場合などで使います。

⑦と⑧は これも ローカル変数に格納します。

 

 

 まだまだ続きます。

これはワールドにあるアクターの座標をScreen座標に変換します。

f:id:hiyokosabrey:20190309113742p:plain

 用意しておいたカスタムイベント InitFancyFollowWidgetで受け取っている変数オブジェクトを変数リストからドラッグ&ドロップして始めます。

f:id:hiyokosabrey:20190309114328p:plain

そこから② Get Actor Location でアクターのワールド座標を取得 → ちょっとだけ位置をずらします。

ConvertWorldLocationToScreenLocation というノードで、3次元のワールド座標を画面で見ている2次元のスクリーン座標に変換します。このノードは、先にGet Player Controller ノードを取り出しておくとスムーズに取り出せます。④→⑤

ここにアクターのポジションを渡してやります。

これをまたローカル変数に取り置きします。⑧、⑨

 

右端のは Returnノードです。 関数には基本このリターンノードを置きます。関数というからには何かしら計算や処理した結果があるので、この Return ノードから、外に出してやるのです。ノード検索キーワードに ピリオド 「 . 」 を入力するとすぐ見つかります。

このリターンノードには アウトプット用のピンを追加できます。リターンノードを選択して、Detailsタブから追加します。

f:id:hiyokosabrey:20190309120515p:plain

 

このリターンノードで返す値がこちら。

f:id:hiyokosabrey:20190309121102p:plain

取り置きしてきた座標やサイズの値をここで一気に計算します。

上段が X座標、下段がY座標用です。それぞれ Clampノードではみだしをカットしています。今回の一番重要な処理だと思います。

Integer型 と Float型 の引き算はそのままではできないので、Integer型をFloat型にキャスト(型変換)してやります。

ノード検索で「 to 」と入力すると ToFloat(Integer) が選択できます。

f:id:hiyokosabrey:20190309122206p:plain

こんなノードが出てきます。

f:id:hiyokosabrey:20190309122432p:plain

全体図はこんな感じ。

f:id:hiyokosabrey:20190309122909p:plain

@ghosticgamesさんのと同じ内容ですが、説明用にコンパクトにしました。

 

これで関数は完成ですが、最後の仕上げとして、この関数をPure型にします。

f:id:hiyokosabrey:20190309125648p:plain

Pureにチェックを付けると・・・

f:id:hiyokosabrey:20190309130113p:plain

グラフにノードとして取り出した際に、実行ピン(白色)がない状態になります。

これを途中だった EventTick処理につないだら完成です。

f:id:hiyokosabrey:20190309130359p:plain

コンパイルして保存したらと閉じてもOK。

 

表示を確認してみる

ワールドに適当なオブジェクトを配置します。

f:id:hiyokosabrey:20190309131522j:plain

実験では、Transformを持ってさえいれば動作すると思います。

配置できたら、手っ取り早いのでレベルブループリントを使って確認します。

 

レベルブループリントの編集方法はこちらから。

f:id:hiyokosabrey:20190309131921j:plain

グラフ編集エディタが開きます。

 ①Create Widget からつないでいきます。

f:id:hiyokosabrey:20190309173918p:plain

アクターに追随するWidgetは、Viewort に追加せずに、先にAdd to Viewport したキャンバスだけのWidget内に持っている CanvasPanel の子供にします。

さらに続き、

f:id:hiyokosabrey:20190309174710p:plain

スムーズにつなげるコツは、白い線は気にせずに、Return Value からドラッグして検索することです。つながるのが約束されているノードが取り出せるので安心です。

 

⑧Set Auto Sizeノードは Inb Auto Size にチェックを付けておきます。

 

⑩はワールドに置いたオブジェクトをドラッグします。
f:id:hiyokosabrey:20190309180355p:plain

この方法は可能なブループリントが限られます。

 

これですべての準備は整いました。

再生してみましょう。適当にカメラを動かしてみてください。

f:id:hiyokosabrey:20190309182035j:plain

対象のオブジェクトが画面から消えても残り続けます。

f:id:hiyokosabrey:20190309182308j:plain

これが Clampを使った移動制限です。

 

一応動画のURLを貼っておきます。

みつまめ杏仁Ver

@ghosticgames さん版

 

細かくオペレーションを書いていたら思ったより長くなってしまいましたが、今回はこの辺まででいったん筆を置こうと思います。

 

次回、計算の仕組みを軽く解説して、オマケの「距離」を表示する部分を書きます。

@ghosticgames さん ステキな教材をありがとう。

 

ではでは

ステキなターゲット表示ライフを!











 

 

 

UMGパーティクル(なんちゃって)

 Widgetでパーティクルっぽいやつを作ったのでTwitter動画をあげたら、いいねしてもらえたので記事にしてみます。さくっとParticleをWidgetに乗せる方法が見つけられなかったのでUMGで作ってみた次第です。

 

変数の型に Box2Dというのがありまして、

f:id:hiyokosabrey:20190214223938p:plain

これを配列にして管理したらいいんじゃね?ってなったのが作り始めたきっかけ。

 

BoxなんでRectangle(長方形)の4頂点のうちの対角の2点を扱うための型です。

f:id:hiyokosabrey:20190214224414p:plain

2D座標ということで xとy を扱うVector2D が2つ束ねられているかたちです。

f:id:hiyokosabrey:20190214225611p:plain

今回これをFloat型が4つあるという認識で扱うことにしました。

 

 ちなみに 他に (x, y, z)の Vector型で2点をまとめた Box型や、BoxSphereBounds型というのもあるようです。ピンの色的にStructure(構造体)の類ですよね。

 

 

パーティクル用に格納する値は3つ。最終回転角と目的地の座標です。

f:id:hiyokosabrey:20190214230317p:plain

ここに粒ごとのゴール位置と何回転するかの値を入れていきます。

 

f:id:hiyokosabrey:20190214231111p:plain

Construct Object From Classノードで WidgetImage を量産して配列に格納。ついでにパーティクルのパラメータを設定。右端のマクロの中身はこんな感じ。

f:id:hiyokosabrey:20190214235115p:plain

発生源から ±500(50.0刻み)の範囲がゴール位置。±3 ~ ±7 回転。

 

次にキャンバスに追加。

f:id:hiyokosabrey:20190215001327p:plain

OffsetPos と、 Color はこのパーティクルWidgetを呼び出すやつがセットするので、受け取るための設定をします。

f:id:hiyokosabrey:20190215001805p:plain

 

パーティクルが発生してから、2秒後に消滅するようにタイマーをセット。Tickのためのフラグを立てて、フェードアニメーションを呼び出します。

f:id:hiyokosabrey:20190215002438p:plain

フェードアニメーションは、キャンバスに対して Render Oapicty を 1.0 → 0.0 にすることでパーティクルを一斉に消します。

 

移動処理のTickは以下。

f:id:hiyokosabrey:20190215003255p:plain

一粒ずつ処理します。ポジションと回転は、WidgetのRenderTransform を使います。それぞれの更新に必要な計算はマクロでやってます。

 

getNewTranslation

f:id:hiyokosabrey:20190215003916p:plain

 

getNewAngle

f:id:hiyokosabrey:20190215004142p:plain

 

これでパーティクルのWidgetは完成。

あとは呼び出すだけ。

 

クリックしてヒットしたときの処理に追加します。

f:id:hiyokosabrey:20190215004931p:plain

パーティクルのカラーは数字の色同様に、あらかじめ配列で持っておきます。

f:id:hiyokosabrey:20190215005102p:plain

 

できあがりです。

f:id:hiyokosabrey:20190215010157g:plain

 

一応動画を確認するためのツイートを貼っておきます。

 

パラメータを工夫すると散り加減が変えられたりして楽しめます。

テクスチャを貼るとまた違った印象になると思います。

 

 

ではでは

 今回はこの辺で

 

ステキなUMGパーティクルライフを!

 

クリックするだけの簡単なゲーム 《改造編》

さっそく前回作ったやつを改造していこうと思います。

 

limesode.hatenablog.com

 

改造ポイントは4つ。

  1. 繰り返し遊べるようにする
  2. タイマーのガクガク震えるのをなんとかする
  3. ちょっと難易度を上げてみる
  4. おまけ

 

まずは、クリアしたあとにボタンを表示したいので、キャンバスにButtonを追加します。作り方は開始時の Click To Start ボタンと同じ作りでいきます。

f:id:hiyokosabrey:20190211142709p:plain

配置したら、Visibility を Collapsed にして非表示にしておきます。

f:id:hiyokosabrey:20190211143238p:plain

 

このボタンを表示する処理をクリア時の処理に追加します。

f:id:hiyokosabrey:20190211144125p:plain

ボタンが表示された次は、ボタンがクリックされた際の処理です。

エディタの Variables の欄に、Buttonが追加されているので選択して Details タブを更新します。

Events の項目に On Clicked があるのでクリックしてイベントノードを取り出します。

f:id:hiyokosabrey:20190211144558p:plain

イベントノードを取り出したら、ひとまずボタンは用済みなので再び非表示に戻してやります。合わせて Complete! の文字も消します。

f:id:hiyokosabrey:20190211150238p:plain

 この後、いろいろと初期化と再セットをしていくのですが、一旦寄り道して、タイマーの改造を進めます。とはいえちょっとブループリントが賑やかになってきたので、改造というよりは新しくタイマー用のWidgetを作ることにします。

 

 いつものようにキャンバスにパーツを配置していくのですが、Epicがストアで無料で提供してくれているプロジェクトを見ていて最近学習した方法があるのでそれでいきます。今頃ですが・・・

 キャンバスのサイズはでデフォルトだと 1080pだったりしてPC環境やコンソールのゲーム機だとそのままで良かったりで、あまり気にしてなかったのですが、Widgetで部品パーツを作る際はなんかもったいないような気がしてました。

f:id:hiyokosabrey:20190211151559p:plain

この右上の Fill Screen を 変更できることができます。

固定サイズなら、Custom、 中身次第にするなら Desired に切り替えるだけ。

f:id:hiyokosabrey:20190211151714p:plain

タイマーはデジタル的に表示したいのでTextBlockをキャンバスパネルの子供にして表示桁ぶん並べます。

f:id:hiyokosabrey:20190211152006p:plain

Desired にしていると キャンバスのサイズは自動的に合わせられます。

ピリオド以外は Is Variable のチェックを付けておきます。

f:id:hiyokosabrey:20190211152240p:plain

 

ブループリントを編集します。

小数点の値を受け取って桁を分解してテキストを更新する関数を用意します。

f:id:hiyokosabrey:20190211153322p:plain

桁の分解は、小数点の扱いがポイントになります。Fraction で整数部分を、Floor で小数以下を切り捨てることで、小数点を境界にして大きく2つに分けることができます。 あとはそれぞれで整数にして計算します。

図で解説してる過去記事を貼っておきます。

limesode.hatenablog.com

これで、数字の文字幅によるブルブルがなくなります。

f:id:hiyokosabrey:20190211154907g:plain

この関数は外から呼ばれますが、初期値のセットも兼ねて、グラフにつないでおきます。

f:id:hiyokosabrey:20190211154354p:plain

タイマーは完成。

 

メインのWidgetに戻って、配置します。

もとのTextBlockで簡易的に作ったタイマーをこのWidgetに差し替えます。

f:id:hiyokosabrey:20190211201015p:plain

中央揃えの場合、Size To Content にチェックを付けるとレイアウトしやすいです。

タイマーの更新処理のところも、このWidgetの関数に置き換えます。

f:id:hiyokosabrey:20190211222746p:plain

100以上の桁表示を作っていないので、99.99カンストするようにしています。表示は見た目に止まりますが変数へのカウントアップは止めていないので、必要であればもう一つフラグを用意した方がよいかも。数字の桁を足す場合、 000.00 となるので、ゼロサプレスの処理が欲しくなる。この辺りは、ランキングの仕組みや、100秒を上回るのが当たり前のような難易度かどうかなどを考えると決まってくると思います。

 

で、寄り道で中断していたRetryボタンの処理の続きです。

f:id:hiyokosabrey:20190211203237p:plain

変数を初期値に戻します。このあと難易度を上げるところで理由を書きますが、ここで StepCount の値を 1始まりではなく 0 にします。

 

あとは、出現イベントから始めれば、通常のゲームループに戻れるはずです。

そのためのカスタムイベントを作って、出現処理のところに脇からつなぎます。

f:id:hiyokosabrey:20190211203936p:plain

 

このカスタムイベントを呼び出すのが、さきほどの ForEachLoop処理の Completeピンです。

f:id:hiyokosabrey:20190211204204p:plain

 

これで大体できました。仕上げに難易度を上げる処理です。

 

その準備として、Integer型の配列変数をもう一つ追加します。

f:id:hiyokosabrey:20190211204532p:plain

次に、先に作っていた NumberPool という Int型の配列も少し初期化の部分を変更します。Event Pre Construct の部分に、配列のサイズを設定するノードを挿入。

f:id:hiyokosabrey:20190211204931p:plain

数字を初期化する関数を改造します。

 

まず、前回と同じく 1~25 までの数字で遊ぶ場合。

f:id:hiyokosabrey:20190211225827p:plain
 先に配列のサイズを決めているので、ここでは Addノードは使えません。代わりにSet Array Elem という、指定した 場所の内容を書き変えるノードを使います。

 今回新しく追加したチェックのための配列をクリアしてから、シャッフルする前の状態を保存しておきます。このクリアは、すぐあとのコピーのために行っていて繰り返し遊ぶときに重要になります。上のようなシンプルなパターンでは、効果が薄いのですが、効果を発揮するのが次のパターン。

 

 もっと難しくする場合。

f:id:hiyokosabrey:20190211225802p:plain
 1~99 までの 25個の数字で遊びます。まんべん無く数字が使われるようにしたかったので25枚目のやつだけループ処理が終わってから乱数をセットしています。

 

 このパターンだと1~25のように パネルの番号とクリックの順番が一致しなくなるので、チェックしやすくするためシャッフルする前の状態が必要になります。

 そこで、チェックのところが、下のように変わります。

f:id:hiyokosabrey:20190211211051p:plain

配列から値を順番に0から取り出してチェックしてゆくので、変数StepCountをゼロ始まりに変更しています。

 

これで改造ポイント1~3完了です。

再生して数字の並びを確認してみます。

f:id:hiyokosabrey:20190211211323p:plain

一番小さい数字からクリックしていくのですが、これが結構難しくて、なかなか30秒切れなかった。

 

youtu.be

 

ここからはおまけです。

 

 上に貼った動画ですでに入ってますが、マウスのホバー処理とエフェクトを出してみます。

 クリックしたときにエフェクトを出せないかと実験してみたけど、ParticleSysytemは、Screen描画してるWidgetよりも手前に描画できないようなので、とりあえず数字がヒットしたタイミングでプリセットの爆発エフェクトをSpawn してみたら派手で面白かったのでそのまま動画を撮りました。

Spawn Emitter at Locationを一つ追加するだけ。(雑い・・・)

f:id:hiyokosabrey:20190212031548p:plain

 再生時のカメラの視界に入るように座標を調整してるだけ。カメラが動くと当然ズレますが何か? WidgetをWorldに置くか、エフェクトをRenderTargetTextureにしてWidgetで利用するしかない感じ?

 

 

 次はホバーエフェクト。当然タッチオペレーションだと無意味なやつです。マウスオペレーションならではですね。動画キャプチャにマウスカーソルが写り込まないので作ってみました。

 そのための仕込みとして、色を付けるための Image をキャンバスに追加します。

f:id:hiyokosabrey:20190212032926p:plain

 カラーは数字の色に合わせて5色用意したいのですが、カラーごとにアニメーションを作るのはナンセンス。というわけで間に1枚専用のパーツを追加して、Render Opacity でアニメーションさせよう、って腹です。カラーは数字の色と同様に配列を用意します。型はLinearColor型。

f:id:hiyokosabrey:20190212201636p:plain

(Font は SlateColor なのは何か理由があるのかな?地味に不便な気がする。)

 

この配列からカラーを取り出してセットする処理を 数字を受け取る関数に追加します。

f:id:hiyokosabrey:20190212202123p:plain

 

このあと、マウスカーソルが乗った時、離れた時、のイベントを用意するのですが、その前に、マウスカーソルが離れたとき用のアニメーションを作っておきます。

f:id:hiyokosabrey:20190212204021g:plain

 RenderOpacity が追加されたことで、Colorのフェードを触らなくていいのが嬉しいです。この RenderOpacity の初期値は 0.0 にしておきます。

 

あとは、マウスイベントをOverrideします。

f:id:hiyokosabrey:20190212204317p:plain

長いプルダウンリストから、 On Mouse EnterOn Mouse Leave を選択します。

 

On Mouse Leave は自身の上にマウスカーソルが乗ってる状態から、離れた瞬間に呼び出されるイベントです。

f:id:hiyokosabrey:20190212205626p:plain

ただ再生するだけでOK。

 

一方、On Mouse Enter は自身の上に マウスカーソルが乗った(入った)瞬間に呼び出されるイベントです。ここでようやく Render Opacity が 1.0 になります。

f:id:hiyokosabrey:20190212205940p:plain

このイベントが呼び出されたときに、アニメーションが残っている可能性があるので、強制的に止めるようにしています。

 これで、カーソルが乗った瞬間に Render Opacity が 1.0 になって、カーソルがはなれたら、0.0に向かってアニメーションするようになります。

 

このくらいでいいかな~と思ったのですが、もう一つ思いついたので作ってみました。

数字の配列を作る関数をちょっといじります。

f:id:hiyokosabrey:20190212212543p:plain

0~25までの 26個の数字を配列に入れるのですが、25個までと決めてあるので、この関数内だけで使えるローカル変数を使います。

f:id:hiyokosabrey:20190212212818p:plain

26個の数字から、一つだけランダムで抜いて25個したら完成です。

 

次に、数字パネルのWidgetに手を加えます。

こんなマクロを用意します。

f:id:hiyokosabrey:20190212213455p:plain

 もうお分かりですね。アルファベットバージョンです。

 しかも、大文字小文字が混在です。A~Z 26文字のうち、1文字だけ足りないので、ジジ抜きのような微妙な緊張感も一緒に楽しめるというやつです。

 

で、このマクロを無理やり↓挿しこみます。

f:id:hiyokosabrey:20190212213855p:plain

どんな感じかというと、

f:id:hiyokosabrey:20190212214114p:plain

ほどよい難易度でなかなか新鮮です。

 

 

 いかがだったでしょうか。ひとまず今回の改造はここまでにしておきます。

 始まりから終わりまで一つのゲームルールができると、いろいろアイデアも出やすい気がします。リザルト画面とかランキングとかネームエントリーとか作ると、どんどんアプリっぽくなっていきそうです。お手付きをカウントしたり一定の間隔以内だったらコンボボーナスとか。一定時間ごとに「急いで!」とか急かしてみたり。ヒント的にパネルが揺れたりしてもいいかもしれない。ベルトコンベアになってても面白そう。もうルールを変えて、何らかのペアをクリックして消しいくのもいいかもしれない。

 イマドキのUE4のトレンドとは逆行してる気がしないではないけど、まぁ楽しんで作れるのが何よりも一番大事だと思う。

 

 

ではでは

ステキはパネルクリックライフを!

 

 

 

クリックするだけの簡単なゲーム

 前回の記事で、パラパラとパネルを出現させるやつで遊んでたら、ビンゴカードになってみたりしたので、さらに調子に乗ってミニゲームにしてみた。もう10年以上前?かな、一度流行ったことがあって、それの再現です。

f:id:hiyokosabrey:20190209210630j:plain

 ルールは、1~25までの数字が書かれたパネルを、順にクリックして全部クリックするまでにかかった時間を競うというものです。あぁ アレか・・・という溜息が聞こえてきそうですが、わりと手軽?に作れたので記事にしてみようかと。ただ記事的にちょっとボリュームがあるかも・・・

 

 作りはシンプルにいきます。作るWidgetは2つだけ。

 

まずは1個目のWidget。キャンバスから。

ここに 120x120の CanvasPanel を一つ。

f:id:hiyokosabrey:20190204001027p:plain

このキャンバスに対して、Image(下敷き) と TextBlock(数字)、Image(カバー用)を子供に します。

f:id:hiyokosabrey:20190204001340p:plain

ここまでは前回と同じ。

このパーツたちに対してアニメーションを用意します。

今回用意したのは以下の4つ。

 

FADE_IN

f:id:hiyokosabrey:20190207003101g:plain

最初の出現用です。この時点で数字はわかりません。

 

 

COVER_OUT

f:id:hiyokosabrey:20190207003403g:plain

ゲーム開始時に 上の FADE_IN 再生後の状態から続く感じで再生します。インパクトを出すために青いカバーを白くします。

 このタイミングで数字と下敷きを表示するのですが、ちょっとだけ仕込みが必要。

f:id:hiyokosabrey:20190209134302p:plain

このあと数字を拡大して消すときに、隣のWidgetにかぶさるとクリック判定を邪魔してしまうのを防ぐために、TextBlockだけは Hit Test Invisible を選択します。

 

 

REMOVE_PANEL

f:id:hiyokosabrey:20190207003933g:plain

クリックして順番が正解した際の演出です。数字だけを消しています。

 

 

FADE_OUT

f:id:hiyokosabrey:20190207004216g:plain

全てのパネルを消した後の演出です。

 

キャンバスはここまで。

次はWidgetブループリント。

 

まずは変数を4つほど用意。

f:id:hiyokosabrey:20190209131348p:plain

 

ちょっと色気を出すためにSlateColor型の変数を配列にします。

f:id:hiyokosabrey:20190207213121p:plain

適当に5色をセット。

 

次に値を受け取る関数を用意します。

f:id:hiyokosabrey:20190207213533p:plain

Integer型のInputピンを2つ。受け取った値は、後で別の場所でも利用するので、すぐに用意しておいた変数にお取り置きします。Indexはパネルの場所を表す値。左上が0、右下が24です。

f:id:hiyokosabrey:20190205002714p:plain

このIndex番号を %5(5で割った余り) すると、下のようになります。

f:id:hiyokosabrey:20190205003147p:plain

この計算した数値をフォントの文字カラーとして配列から引っぱり出しすと左の列から順に色が変化することになります。

 

Numberは表示するための数字です。

 

関数の次は、イベントディスパッチャーを2つ用意します。

f:id:hiyokosabrey:20190207214725p:plain

アニメーションの終了通知と、クリックされた際の応答に使うためです。

応答用のは、値を送り返すのでInputピンを2つ追加します。

f:id:hiyokosabrey:20190207214939p:plain

 

アニメーションを再生するためのカスタムイベントを用意していきます。

最初の登場演出用。

f:id:hiyokosabrey:20190209132003p:plain

出現時、まだクリックを受け付けたくないので、フラグを False にしておきます。

※このフラグは後述のマウスイベントの処理のところで活躍します。

 

このアニメーションの後に、『 Click to Start 』って出したいのでアニメーション終了通知のために、イベントディスパッチャーを呼び出し(Call)ています。

このWidgetは画面に 25個も表示するので、通知処理は最後の1個だけで十分です。前の24個は通知処理しなくてもいいので、カスタムイベントのInputピンにBooleanを一つ追加しています。

ちなみに、このイベントを呼び出す際は下のようなノードになります。

f:id:hiyokosabrey:20190207222535p:plain

 

次は、ゲーム開始のための表示演出イベント。

f:id:hiyokosabrey:20190209134805p:plain

ここから、マウスクリックを受け付けるので、フラグを True にします。

 

次は、数字がヒットして、消えるときの表示演出イベント。

f:id:hiyokosabrey:20190209135301p:plain
ここで マウスクリックを受け付けたくないのでフラグを False にします。

 

 

次のイベントは、ゲーム終了時の表示演出イベント。

f:id:hiyokosabrey:20190207224027p:plain

 

これで4つのアニメーション再生の準備は整いました。

 

最後に、マウスクリック時の処理を用意します。

関数を作るところに Override ボタンがあるのでそこから

f:id:hiyokosabrey:20190207224936p:plain

On Mouse Button Down を選択してから編集します。

f:id:hiyokosabrey:20190209140223p:plain

 クリック受付するかしないかのフラグをみて分岐させます。

 

これでパネル用Widgetが用意できました。

 

メインになる親のWidgetを用意します。

キャンバスには パネルを並べるための WrapBoxを一つ。

f:id:hiyokosabrey:20190207231937p:plain

中央になるように調整します。

f:id:hiyokosabrey:20190207231820p:plain

f:id:hiyokosabrey:20190207233215p:plain

パネル1枚が 120x120なので、 120x5 + スキマ から指定幅を計算。指定幅を越えたら改行するので、6個目が並ばないように指定します。

Size To Content にチェック付けると、 Size XとSize Y は無意味なので初期値でOK。

 

次に、タイマー用の TextBlockを配置します。

f:id:hiyokosabrey:20190207234327p:plain

とりあえず、WrapBoxのすぐ上あたりに。

次は、クリアしたときの「 Complete! 」をTextBlockで配置。

f:id:hiyokosabrey:20190207234608p:plain

これはクリアしたときまで取っておくので、配置したらVisibilityの設定を非表示(Collapsed)にしておきます。

 

あとは、Click To Start のボタンを配置します。

f:id:hiyokosabrey:20190208002547p:plain

覆うように大きく大胆に広げます。

f:id:hiyokosabrey:20190208002557p:plain

配置したButtonの子供にTextBlockを置くと、中にテキストを置けます。

Hierarchyはこんな感じ。

f:id:hiyokosabrey:20190208003031p:plain

f:id:hiyokosabrey:20190208003505p:plain

 

このままだと後ろが見えないので、Buttonのカラーをカスタマイズします。

とりあえずカラーは 黒 でアルファが 0.5 くらいにします。

Buttonコンポーネントは専用のイベントをいくつか持っているので、Normal、Hovered、Pressed の3か所を触ります。

f:id:hiyokosabrey:20190208004043p:plain

あくまでもボタンの地の色で、テキストブロックのカラーは変化しません。

f:id:hiyokosabrey:20190208220346p:plain

Buttonを大きくするのは、数字のパネルがクリックを受け付けてしまうのを防ぐ目的もあります。

 

キャンバスはこの辺にして、ブループリントを編集します。

 

まずは、数字を配列にセットする関数。

f:id:hiyokosabrey:20190208220655p:plain

配列のIndex番号は 0(ゼロ)始まりだけど、ゲームの表示は 1(イチ)からなので、ForLoopを使って、1~25 の数字を格納。

 

配列をシャッフルする関数。

f:id:hiyokosabrey:20190208220917p:plain

配列にShuffleノードをつなぐだけで配列の中身をシャッフルしてくれます。

 

上の2つの関数を Event Pre Construct につないでおきます。

f:id:hiyokosabrey:20190208223307p:plain

クリックしたパネルの数字が順番通りかどうかチェックするための変数もここで初期値をセットしておきます。

このイベントは 、ブループリントをコンパイルした時点で一度処理されるという特殊仕様です。結構便利な使い方ができるので後ほどご紹介します。

 

次は数字パネルのWidgetを並べる処理。

f:id:hiyokosabrey:20190208225251p:plain

真ん中付近の配列の作り方は、

f:id:hiyokosabrey:20190208225442p:plain

Create Widget ノードの ReturnValue ピンから Promote to Variable(変数へ昇格) して、一旦 "専用の"の変数を作成します。

f:id:hiyokosabrey:20190208225734p:plain

f:id:hiyokosabrey:20190208230257p:plain

Variablesのリストに追加されているので、Detailsから配列に変更します。

f:id:hiyokosabrey:20190208230311p:plain

この操作は、一旦グラフから取り除いてからやると、エラーチェックが走らないのでオススメ。つながった状態でやると警告されます。

 

で、数字パネルをWrapBoxに追加した後は、数字パネルを出現させます。

f:id:hiyokosabrey:20190209034203p:plain

出現開始をする前に、アニメーション終了の通知が欲しいので最後の数字パネルにバインドを施しておきます。そして再び ForLoopを使って一斉に数字パネル全部に数字を渡しつつFADE_INのアニメーションを再生させる関数を呼び出しています。

その関数はこちら。

と、その前にカスタムイベントを準備。

f:id:hiyokosabrey:20190209034855p:plain

数字パネルのバインドで送り返してもらう値がInteger型で2つあります。それを受けるのであらかじめこちらでも2つのInputピンを装備させます。このイベントは後から作っても大丈夫。でも少しだけ手数が増えます。その理由はこれ↓

f:id:hiyokosabrey:20190209170308p:plain

関数の中にカスタムイベントを置くことはできないので、替わりに CreateEventノードをつなぎます。

f:id:hiyokosabrey:20190209170731p:plain

バインドノード経由のイベントをつなぐと、Select Function というプルダウンリストが出てきます。そこでイベントディスパッチャーの値を渡せるイベントを指定するのですが、Inputピンの型と数が一致していないと、プルダウンに出てきてくれないのです。

 

そのカスタムイベントの続きはこんな感じになります。

f:id:hiyokosabrey:20190209172342p:plain

クリックした数字パネルから応答が返ってきた値を比較して、同じなら成功なので、クリックした数字パネルの演出イベントを呼び出します。まだ数字パネルが残っているかどうかチェックして、残っていれば順番をチェックするカウンター用の変数を一つ進めます。

 なんとか 25までクリックできたら、終了処理に移ります。続き。

f:id:hiyokosabrey:20190209174108p:plain

Complete! のテキストを表示して、残ったパネルを消すイベントを呼びます。

 

そろそろ大詰め。ゲーム開始用のイベント。

編集モードをDesignerに変えて、Click To Start 用のButtonパーツを選択。

Detailsタブの一番下に、グラフにイベントを生成するボタンがあるので、On Clicked をクリック。

f:id:hiyokosabrey:20190209180903p:plain

f:id:hiyokosabrey:20190209181039p:plain

ここにゲーム開始の処理をつないでいきます。

f:id:hiyokosabrey:20190209175430p:plain

最後にフラグを True にしていますが、これはタイマー表示に使います。

 

タイマーは  ゼロから始まる~ のでEvent Tick で Float型の変数に加算していきます。

f:id:hiyokosabrey:20190209182303p:plain

ここで便利なノードが、 FormatTextノード。

「単位」をくっつけたり、文章の途中に任意のテキストを挿入するといった使い方ができます。

  詳細は公式サイトにあります→  ユーザーに表示するテキストの書式設定

 こちらのブログエントリーもおすすめです→ UE4ワイルドカードを使った書式設定で文字列を作成する

 

イメージしやすそうな例で説明を書いてみます。

 

取り出した直後はこんな形。

f:id:hiyokosabrey:20190209183359p:plain

ここにピンを増やすのですが、増やし方が変わっています。

例えば、こんな文章があったとします。

 

 おおトンヌラよ、しんでしまうとは ふがいない。

 

この名前のところを、半角の中括弧(波括弧)でピン名を挟んだ形、{ ピン名 }に置き換えます。

 

 おお{CharacterName}よ、しんでしまうとは ふがいない。

 

これを FormatText に入力してみると、こうなります。

f:id:hiyokosabrey:20190209184036p:plain

新たに入力ピンが追加されます。

{ピン名} はいくつでも配置できます。

f:id:hiyokosabrey:20190209184527p:plain

今回は、簡単に Float型の値に、 sec をくっつけています。

{Time}sec

 

これで一通りの用意ができました。

レベルブループリントからViewportに追加して、マウス制御用のノードをつないで、

f:id:hiyokosabrey:20190209185732p:plain

再生してみます。

www.youtube.com

 マウスカーソルがキャプチャできなかったのでちょっとわかりにくいかもですが、一通り遊べてます。

も少し整えないといけないところがあるのですが、そのあたりは次回に回そうと思います。

何度かチャレンジしてみて、なかなか12秒を切れなかった。パネルの大きさとかも影響するかもしれない。数字の色とか、フォントをいじるとまた違った難易度になると思います。一度つくってみるといろいろアレンジしたくなるので楽しいですね。

 

ではでは

ステキなパネルクリックライフを!

 

 

 

 

パラパラとアイテムが表示されるやつ

 節分も過ぎて少しづつ暖かくなっていくのは嬉しい。今までこんな風に感じたことなかったのは、寒さに弱くなったせいかもしれない。

 

 さてさて今回は、登場時に動きのあるUIを考えてみました。

グリッド状に並んだパネルが順にめくられていくやつ。

f:id:hiyokosabrey:20190205045827j:plain

 

作りは結構単純です。

まずは、パネル1個分のWidgetを用意します。

表示する内容はパネルのWidgetが受け取るものによって変わります。今回はナンバー受け取る仕様で作りました。アイコンだったらテクスチャを受け取ってもいいです。

 

さっそくキャンバスから。

サイズは 120x120にしました。

f:id:hiyokosabrey:20190204001027p:plain

このキャンバスに対して、Image(下敷き) と TextBlock(数字)、Image(カバー用)を子供に します。

f:id:hiyokosabrey:20190204001340p:plain

これにアニメーションを付けます。

手前に表示するカバー用のImageが最大サイズになったら、その下に隠すように他のパーツを表示開始。

f:id:hiyokosabrey:20190204002032p:plain

カバー用のImageは、Pivotを左上にしています。

f:id:hiyokosabrey:20190204001816g:plain

 

キャンバスはこれで終わり。次はブループリント。

値を受け取ってキャンバスに反映させる関数を用意します。

f:id:hiyokosabrey:20190204002708p:plain

 

つぎに、イベントディスパッチャーを用意します。

f:id:hiyokosabrey:20190204225623p:plain
これはアニメーションの終わりを通知するためのものです。

 

つぎに、アニメーションを再生するイベント。

時間差を作るためにDelayノードに頑張ってもらいます。

f:id:hiyokosabrey:20190204225604p:plain

こういったアニメーションなど演出に時間が存在する場合、ちゃんと終わってから操作を受け付けないとカッコ悪いUIになってしまいます。また、アニメーションの尺調整をしても長さに応じて自動で処理したいもの。そこで setTimerByEventノードを使って、アニメーションの終了タイミングで通知をします。とはいうものの、たくさんのパネルを動かすことになるので、このタイマーイベントを全てのパネルで実行するのは処理負荷になります。最後のパネルだけでいいので、やるやらないを分岐するためのブール値も受け取れるようにしました。ちょっと処理が増えましたが、このパネルのWidgetが汎用的に利用できるようになります。

 

これで、パネルのWidgetは完成です。

つぎにパネルを並べるための親Widgetを作っていきます。

 

キャンバスに Wrap Box を一つ配置します。

f:id:hiyokosabrey:20190204231713p:plain

f:id:hiyokosabrey:20190204231852p:plain

5x5 の 25枚を並べるので、Details タブ で調整します。

f:id:hiyokosabrey:20190204233027p:plain

Explicit Wrap Width にチェックを付けると、指定した幅(Wrap Width)を越えないように改行してくれます。今回用意したパネルは幅が 120 なので、120x5=600

ここにパネル間のスキマ  2x4=8 を合わせて 610 にしています。

 

ではBlueprintでここにパネルを追加していきます。

f:id:hiyokosabrey:20190204234103p:plain

ForLoopでパネルを生成しつつ、順次配列に追加していきます。配列に積んでおくことで、このForLoop処理を抜けた後でも扱うことができます。

この段階で、25枚の透明のパネルたちが、数値をもらって待機完了です。

 

パネルに待ち時間をセットする関数を用意します。

f:id:hiyokosabrey:20190205001502p:plain

剰余(%)と除算(÷)を使って左上から右下に向かうように計算しています。

Index番号は左上 0 で始まって、右下に向かって増えていきます。

f:id:hiyokosabrey:20190205002714p:plain

Index番号に対して、5の剰余を求めると、

f:id:hiyokosabrey:20190205003147p:plain

一方の除算はというと、

f:id:hiyokosabrey:20190205003420p:plain

整数の割り算は端数は切り捨てられます。

この2つを加算すると、

f:id:hiyokosabrey:20190205003923p:plain

これに適当な小数を掛けて待ち時間にしてやるのです。

 

 この関数をパネルの数ぶん呼び出すことになります。

 

カスタムイベントにしました。

f:id:hiyokosabrey:20190205000909p:plain

全25枚のパネルのうち、24枚を ForLoop ノードで回して、最後のパネルだけ手動でやる感じ。ここでようやくイベントディスパッチャーをバインドします。

 


 これで並べるWidgetは完成です。

 

Viewportに追加してテストしてみます。

レベルブループリントを編集。

f:id:hiyokosabrey:20190205004847p:plain

 

再生して、ウィンドウをフォーカスした後、スペースキーを押すとこんな感じ。

f:id:hiyokosabrey:20190205010004g:plain


剰余(%)のみだった場合。

f:id:hiyokosabrey:20190205051617g:plain

 

除算(÷)のみの場合。

f:id:hiyokosabrey:20190205051055g:plain

 

アニメーションでも見た目をアレンジできるので楽しいですね。

 

たにみに数字をシャッフルすると・・・

f:id:hiyokosabrey:20190205052948g:plain

 

なんだかBINGOカードに見えてきたので、BINGOカードにでもしてみようかな。

 

今回はこの辺で

 

ではでは

ステキなパネル出現ライフを!

 

 

ついでに丸い形のミニマップ表示を作ってみる

 前回作ったミニマップ表示は Widgetの Pivot を使ったので、 今回マテリアルを使って試してみました。マテリアルだとマスクが使いやすいのでカタチを丸くしたりできま
す。でもマスクできるのはマテリアル内のテクスチャなので、マーカー等の目印やアイコンを置きにくいのが難点。Widgetのキャンバスにマスクが適用できればいいのですが・・・。

 

f:id:hiyokosabrey:20190120160103p:plain

 仕組みはシンプル。UVはそもそもが 0~1 なので Pivotと同じような座標で扱えます。前回作ったWidgetを複製して改造することにします。改めてリンクを貼っておきます。

 

limesode.hatenablog.com

 今回の記事だけでも試せるように構成してみたつもりですが、細かい説明は端折っていたりするので、興味のある方は過去記事も覗いてみてください。

 

 

 拡縮については、TexCoord の Tiling を変更します。ただ このTiling の扱いが、Scale と違うので少し計算が必要になります。

 

まずはマテリアルを新しく用意。

f:id:hiyokosabrey:20190118002438p:plain

 テクスチャを受け取る Texture Sample Prameter 2D と、Scalar Param が3つ。

それぞれUV範囲の大きさと位置を受け取ります。正方形を想定してるので、サイズに関してのパラメータは一つです。

 

 

 

まん丸のマップにしたいので、キャンバスパネルを正方形にします。

f:id:hiyokosabrey:20190119212425p:plain

中の Image には 新しく用意したマテリアルをセットします。

今回マテリアルで拡縮するので サイズは固定です。

 

変数は以下を用意しました。上の4つはVector2D型です。

f:id:hiyokosabrey:20190119213104p:plain

あとからここにもう一つ加えます。

 

 

setMapTexture

テクスチャを受け取る関数を改造します。

f:id:hiyokosabrey:20190120113803p:plain

  マテリアルを扱うので、Get Dynamic Materialノードで、マテリアル インスタンス ダイナミックを作って、変数化しています。計算用の値をここで準備しています。

 

 キャンバスのサイズは、一旦キャストしないと参照できません。

f:id:hiyokosabrey:20190119224255p:plain

 最終的に決め打ちで固定の値をセットしてやればいいと思いますが、開発中はレイアウト調整で細かく変更したくなると思うので、ブループリントのことを気にせず触るようにするためのテクニックです。

 

 

updateTranslation

 次はマップを任意の位置に移動させる関数。

f:id:hiyokosabrey:20190119224837p:plain

 XY,座標を受け取って、テクスチャのUVに変換しています。実際はもっと補正が必要だと思います。今回は左上が (0, 0) の座標空間で、マップの中心が テクスチャサイズの 1/2 を前提としてます。マップテクスチャの1ピクセルがワールド空間のどれくらいかによって補正を掛けることになるので、テクスチャのサイズ選びはビジュアルも含めて、UI設計が難しいお仕事の中でも上位レベルになります。

 前回のはマウスで操作するので、用途としてはインベントリ画面とかポーズメニュー画面なんかでマップ確認というイメージを想定していました。

 

 

 Pivot の計算は不要なので、関数 updatePivot は不要になりました。

 替わりに、新しくマクロを作ります。マウスドラッグに追随する際の表示倍率に合わせた移動値を計算します。

f:id:hiyokosabrey:20190120005446p:plain

 このマクロは、ポジションを変更するイベントで使います。

 

 

 

ここからはマウスイベント用の関数を見ていきます。

 

OnMouseButtonDown

これはそのまま変更なしです。

f:id:hiyokosabrey:20190112095622p:plain

 

 

OnMouseButtonUp

これもそのまま。

f:id:hiyokosabrey:20190112102524p:plain

 

 

OnMouseMove

ここに、新しく作ったマクロを仕込みます。Pivotの更新する関数は外しました。

f:id:hiyokosabrey:20190120010529p:plain

マクロにしなくてもいい気がしますが、コンパクトになるので。

 

 

OnMouseWheel

ホイールを転がしてスケールを変更する際の符号が逆になります。

f:id:hiyokosabrey:20190120011007p:plain

  テクスチャを切り出すときのサイズは、Tiling で指定します。表示サイズが変わらないので、値が小さいほど切り出す範囲が小さくなって、結果的に拡大することになります。

 例えば 1024x1024のテクスチャがあったとします。表示サイズはちょうどその 1/2の 512x512だった場合、下図のようなイメージになります。

 

f:id:hiyokosabrey:20190120020021p:plain

表示倍率が上がるほど、Tilingの値は小さくなっていきます。虫眼鏡で拡大するのと同じ要領ですね。

 

 

 最後の関数

OnMouseLeave

EventGraphにおかれるやつ。

最後にあったPivot更新の関数は不要になりました。

f:id:hiyokosabrey:20190120111819p:plain

 

 

これで、関数&イベントの改造は終了です。

 

仕上げに、 EventConstruct にあった Pivotの更新を、マテリアルへの更新に変えます。

f:id:hiyokosabrey:20190120112534p:plain

マップの表示の縦横比が 1:1 の正方形用なので、Vector2D の持つ値を一つだけ使います。

これでWidgetは完成。

 

 

テスト用のレベルブループリントも少しだけ変更になります。

今回作ったWidget用だけにまとめるとこんな感じです。

f:id:hiyokosabrey:20190120115442p:plain

テストしてみましょう。

f:id:hiyokosabrey:20190120120124g:plain

うまく動いているようです。

 

丸い形をしているので、回転させることができます。

この形だとたいていはHUDに置かれるので、マウスで操作できなくてもよいですね。

 

 

ということでもう少し遊んでみます。

レベルブループリントに変数を一つ追加。スタート位置 兼 補正値です。

関数の前でセットしてそのまま関数に渡してます。

f:id:hiyokosabrey:20190120142806p:plain

 

あとは、EventTick でカメラ位置と回転値を拾って、Widgetに渡します。

f:id:hiyokosabrey:20190120142943p:plain

 

マップには、 Plane のメッシュを中央に置いて、XY方向に それぞれ100倍して、マップのテクスチャを貼りつけます。

f:id:hiyokosabrey:20190120153807p:plain

f:id:hiyokosabrey:20190120143324p:plain

もともと置いてあった Floor はこんなサイズ。

f:id:hiyokosabrey:20190120144147p:plain

Plane を 100倍すると、端っこのポジションは ±5000 になります。1辺の長さが 10000 になるので、テクスチャサイズ 2048 を割ると、 0.2048 になります。取得したカメラポジションに対して掛けてから、Widgetに渡します。

f:id:hiyokosabrey:20190120144507p:plain

 

あと、マップのWidgetには 中央に ▲ を置いておきます。

 

プレイしてみると・・・。

f:id:hiyokosabrey:20190120145142j:plain

f:id:hiyokosabrey:20190120145203j:plain

 静止画ですが、いい感じにグリグリと連動して動いてます。

マップ部分だけGIF化しました。

f:id:hiyokosabrey:20190120151600g:plain

 FPSカメラなので、床から離れているのもあって見た目とマップ位置のギャップを感じますが、いろいろ補正を試してみると何か発見があるかもしれません。

 

 TPSだと、キャラの向きとカメラの向きを反映させるために、カメラのFOVに合わせた扇形の図形を重ねたりします。

 

なんとか形になったので、今回はこの辺で。

 

ではでは

ステキなミニマップライフを!