投稿

2017の投稿を表示しています

[UMG+Slate]プリミティブを描画するウィジェットを作成する

イメージ
この記事はUE4 Advent Calender 2017の24日目の記事です。
今回紹介する内容を応用すると、このような変なウィジェットを作ることができます。
このウィジェットの具体的な実装方針については記事内で触れません。代わりに、完成したプロジェクトをダウンロードできるようにしたので、そちらを参照いただければ幸いです。※実装が悪く、パフォーマンスが非常に悪いので、実装方法の参考程度に留めておいてください。

はじめに 記事内で表記する用語を統一します。グラフィックスプログラミングにおける「プリミティブ」とは、頂点を元に描画される線やポリゴン等の図形を指しますが、この記事の中ではDrawBoxノードやDrawTextノード、MakeBox関数やMakeSpline関数(ノード、関数の詳細は後ほど)によって描画されるものに対して呼称するものとします。
また、本記事で用いるFSlateDrawElementクラスは、UE4のバージョンによる変更が多いクラスです。記事では、執筆時点で最新のUE4.18を用いることとします。

プリミティブを描画する まず、図1のように、プリミティブを描画するウィジェットを作成します。コンテンツブラウザ上で右クリックして、コンテキストメニューの[UserInterface][WidgetBlueprint]を順にクリックします。新しく作られたCustomUserWidgetの名前を、ここでは「UI_PaintTest」とします。

次に、UI_PaintTestを開き、デザイナータブからグラフタブへ移動します。Functionsの横にある「Override」をクリックするとオーバライド可能な関数の一覧が表示されるので、OnPaintを選びます。

OnPaint関数内では、引数となるContextを受け取ってDraw~関数を呼び出すことで、プリミティブを描画することができます。今回は矩形を描画するためのDrawBoxを紹介します。図3のように実装しました。Brush引数にはSlateBrushAssetを設定する必要があるので、一旦コンテンツブラウザに戻ります。

図4のように、コンテンツブラウザ上で右クリックして、コンテキストメニューの[UserInterface][SlateBrush]を順にクリックします。その後、新しく作られたSlat…

[UE4] UE4.17リリースノートピックアップ

イメージ
今回は、UE4.17の新機能をピックアップして紹介します。この記事は、ドキュメントのリリースノートにもとづいて作成しました。

[UnrealC++]UPROPERTYマークアップされた変数のGetterとSetterが定義可能に ・Native member variables can now make use of uproperty markup to be exposed to Blueprints via ufunction accessors even if the property had been previously exposed directly using BlueprintReadOnly or BlueprintReadWrite markup.

UPROPERTYでマークアップされたネイティブメンバ変数に対して、GetterとSetterを定義できるようになりました。単純なGetとSetでは、変数の値を取得や変更することしか行えませんが、GetterやSetterを使うことで、値の取得や変更の前処理をできるようになりました。例えば、不正な値をはじくといった処理をスマートに記述できるようになります。
GameModeBaseを継承したクラスで、次のようなコードを用意しました。
BlueprintGetterの作り方は、Getterの対象となる変数の型(ここではint32)を返す関数に、UFUNCTIONでBlueprintGetterを指定し、Getterの対象となる変数のUPROPERTYにBlueprintGetter=<Getter関数名>のように記述します。BlueprintSetterも同様に、Setterの対象となる変数の型を引数にもつ関数に対して、UFUNCTIONでBlueprintSetterを指定し、Setterの対象となる変数のUPROPERTYにBlueprintSetter=<Setter関数名>のように記述します。
NumのUPROPERTYにはBlueprintReadOnly/ReadWriteといった指定子がありませんが、指定したときのようにGet/Setノードが現れます。

Get/Setノードが実行されると、Getter/Setter関数が呼び出され、0以上の値になるようなフィル…

[UnrealC++] ゲームウィンドウ内にSlate Widgetを埋め込む

イメージ
はじめに 前回は、一般的なGUIを外部ウィンドウにまとめることで、主に非ゲーム的な用途を想定した、UE4アプリケーションの作成方法について紹介しました。今回は、外部ウィンドウにGUIをまとめるのではなく、ゲームウィンドウ内にGUIを埋め込むことで、一般的なGUIを持つUE4アプリケーションを作成する方法の一つを紹介します。なお、記事内での「一般的なGUIアプリケーション」とは、WPFやQtといったGUIフレームワークによって作成された、例えば図1のようなものを想定しています。
SWidget直接挿入による埋め込み ゲームウィンドウ内にGUIを埋め込む方法の一つとして、ゲームウィンドウを構築しているSWidgetに対し、埋め込みたいSWidgetを子ウィジェットとして挿入する方法が考えられます。図2は、PIEでゲームを開始し、現れたゲームウィンドウに対してWidget Reflectorを適用した結果です。SViewportに至るまでのツリー構造の中に、挿入可能なSVerticalBoxなどがいくつか確認できます。
ルートであるSWindowやGameViewport(SViewport)はGEngine経由でアクセスすることができるため、挿入可能なSVerticalBoxなどは容易に取得することができ、実際に埋め込みたいSWidgetの直接挿入も実現することができます(できました)。 しかし、この方法には問題があります。ゲーム開始モード(Selected Viewport, New Editor Window (PIE), Standalone, ......)によって、ゲームウィンドウを構築するウィジェット構成が異なるためです。図3はSelected Viewportでゲームを開始した場合の、Widget Reflectorの結果です。

図2と比べると、SViewportに至るまでの構成がまったく異なります。これを解決するためには、ゲーム開始モードごとのウィジェット構成を考慮する必要があります。それ以外にも、フルスクリーン時にクラッシュしたり、UE4Editor自体にSWidgetを埋め込んでしまうとPIE終了後も残り続けてしまう、などの問題があります。 このような問題が山積みとなっており、埋め込みたいSWidgetを直接挿入するのは、実質的に不可能と考えられ…

[UnrealC++]SWindowで対話可能な外部ウィンドウを作る

イメージ
はじめに UE4を非ゲーム的な用途のアプリケーションを作成する時、一般的なアプリケーションのようなGUIを作りづらいという問題に直面したことがあります。UMGで作るのが最も簡単ですが、外部ウィンドウにGUI部分をまとめて、ウィンドウ同士で対話するというやり方もあります。
今回は、図1のような、「ボタンを持ったウィンドウ」を生成し、ボタンが押されると特定の処理が実行されるような、対話可能な外部ウィンドウの作り方を紹介します。

実装 C++でMyGameInstanceを実装する Slateを用いるので、今回もSlateモジュールを追加します。.Build.csを次のようにします。
次に、GameInstanceを継承したクラス(名前をMyGameInstanceとします)を作成します。GameInstanceを選ぶ理由ですが、GUIをまとめたウィンドウ(以降、GUIウィンドウと略します)はレベルに依存せず、起動から終了までどこからでもアクセスできる必要があり、そのような機能に最も適当と考えたからです。
.hを次のように実装します。
OnClickedはGUI ウィンドウ上のボタンを押されたときの関数です。Blueprintで継承して、BP上で実装します。
.cppを次のように実装します。
GameInstanceのInit関数をオーバーライドして実装しました。
関数の本体は、SetTimerForNextTickを使って実行を遅延させます。これは、Init関数が呼び出される時点でゲームのウィンドウがまだ完全に生成されていない問題を回避するためです。
次に、SNewによってSWindowを生成します。SWindowのコンテンツには、SButtonを与えます。SButtonのOnClicked_Lambdaには、BlueprintImplementableEventに指定したOnClicked関数を呼び出すようなラムダ式を与えます。
最後に、FSlateApplication::Get().AddWindowAsNativeChildに、GUIウィンドウであるwindowと、ゲームウィンドウである GetGameViewportClient()->GetWindow()を親ウィンドウとして与え、ウィンドウ同士を関連付けます。
これで、C++部分の実装は終了です。

[UMG]NativeWidgetHostを継承してカラーピッカーを作る

イメージ
今回は、NativeWidgetHostというウィジェットを使って、動画のようなカラーピッカーをUMG上で使えるようにする方法を紹介します。

はじめに SWidgetとUWidget UE4エディタは多数のUIコンポーネント(ラベル、テキストブロック、ボタン、あるいはそれらを組み合わせたもの)で構築されており、それらUIコンポーネント全てがSWidget(を継承したクラス)です。図1はWidget Reflectorというツールを用いて、エディタを構築するウィジェットを調べたものです。WidgetName一覧にある接頭辞にSがついたクラスは全てSWidgetを継承したクラスであり、UE4エディタがSWidgetを継承したクラスの組み合わせによって構成されていることがわかるかと思います。
一方で、図2はUMGのパレットを示したもので、この一覧にあるウィジェットがUWidgetです。UMGを使っていて気づいた方もいらっしゃるかもしれませんが、図2にあるウィジェット、例えばComboBoxやCheckBoxといったウィジェットのデフォルトの外観は、UE4エディタにある同じ機能を持ったウィジェットとまったく同じものです。それもそのはず、UWidgetはSWidgetをUMG向けにラップしたクラスであり、UWidget内部で使われているSWidgetと、UE4エディタで使われているSWidgetは同じものであるためです。
NativeWidgetHostとはUE4のドキュメントには、このように書かれています。 「子スレート ウィジェットを 1 つ格納できるコンテナ ウィジェットです。UMG ウィジェット内部にネイティブ ウィジェットをネスティングすることだけが必要な場合に使用します。」 ここで「子スレートウィジェット」とは、SWidgetを継承したクラスのことを指し、UWidgetとして公開されていないSWidgetをUMG上で使いたい場合に用いる、と説明されています。実は、SWidgetをゲーム中に使うこと自体は、UMGを仲介する必要がありません(参考)。しかし、この方法ではUIデザインはまともにできないため、より扱いやすくするためにUMGに公開する必要があります。そのようなとき、UMGに公開する方法の1つとして、NativeWidgetHostが用意されています。 先程「…