[UMG]インベントリシステムを作る

この記事を読むと、UMGで図1のようなインベントリシステムが出来上がります。
図1 インベントリシステム

はじめに

インベントリシステムは多くのゲームで使われるシステムで、UE4でこれを実現する方法を調べると、既にたくさんの人が実装方法を紹介しています。特に役に立ったのは以下の2つのページです。
作り方としてはドラッグ&ドロップ関連の、既に用意された関数を少し実装することで、簡単に作ることができるようです。今回は、図1のような「要素の交換が可能」なインベントリシステムの実装方法を紹介します。

ドラッグ可能ウィジェットの作成

まずは、ドラッグ可能なウィジェットを作成します。図1における、カーソルで掴んで移動しているウィジェットの部分です。はじめに、UI_DraggableElementという名前のWidgetBlueprintを作り、図2のようなレイアウトにします。
図2 UI_DraggableElementのレイアウト

SizeBoxを図2の赤い部分のように設定します。サイズは128*128で固定して、SizeBoxにマウスオーバーしたときにカーソルの見た目を手形にするために、CursorをHandにします。
SizeBoxの子ウィジェットにはNamedSlotを配置します。ここでのNamedSlotは、UI_DraggableElementの見た目をHUD上で変更するためのものです。

次に、図3のようにOnMouseButtonDownを実装します。
図3 OnMouseButtonDownの実装

関数のオーバーライド一覧からOnMouseButtonDownを選んで関数を作成し、次に、その関数をDetectDragIfPressedノードを用いて図3のように実装にします。これで、UI_DraggableElement上でマウスの左ボタンが押されると、DetectDragが通知されるようになります。

次に、図4のようにOnDragDetectedを実装します。
図4 OnDragDetectedの実装

こちらも同様に、オーバーライド一覧から作成します。関数はCreateDragDropOperationノードでDragDropOperationオブジェクトを作成し、それを返します。ここで作成したDragDropOperationは、ドラッグ&ドロップ関連の様々な関数が実行される際に、引数として渡され、その実装で自由に使用することが出来ます。
このノードの引数の意味は次のようになります。
  • Class:作成するDragDropOperationのクラスです。追加の引数が必要な場合などは、DragDropOperationクラスを継承したクラスを指定します。今回は変更しません。
  • Tag:任意の文字列です。OnDropやOnDragCanceledでユーザーの自由な実装のために用いられます。何を与えてもいいですが、今回は使用しません。
  • Payload:任意のUObjectです。OnDropやOnDragCanceledでユーザーの自由な実装のために用いられます。何を与えてもいいですが、今回はGetParentノードによって、このウィジェットの親となるPanelWidgetを与えます。これは後の実装で、入れ替え処理等に用います。
  • DefaultDragVisual:ドラッグ中に表示されるウィジェットです。今回はこのウィジェットを表示するので、Selfを指定します。
  • Pivot:ドラッグ中に表示されるウィジェットの起点です。例えば、CenterCenterを指定すると、DefaultDragVisualの中心にカーソルが合うようになります(Offsetが0のとき)。今回は変更しません。
  • Offset:カーソルとPivotとの位置のオフセットです。今回は変更しません。
CreateDragDropOperationノードの後にRemoveFromParentノードを実行するのは、ここでウィジェットをパネルから取り除かないと、パネル内にドラッグ中のウィジェットが残ってしまうためです。気になる方は、RemoveFromParentをはずして実行してみると、必要な理由がわかるかと思います。

最後に、図5のようにOnDragCanceledを実装します。
図5 OnDragCanceledの実装

これは、ドロップが失敗したときに実行されるイベントです。ドロップが失敗したということは、ドロップ可能スロットへの移動が失敗したことを意味するので、元のスロットへ戻る処理をする必要があります。元のスロットへ戻るには、先程CreateDragDropOperationノードのPayloadに指定した親のパネルに自分自身を追加すればよいので、Operation引数からPayloadを取得・キャストしてAddChildにSelfを与えます。
これで、UI_DraggableElementは完成しました。

ドロップ可能スロットの作成

次にドロップ可能なウィジェットUI_DroppableSlotを作成します。これは図1の6つの白いスロットの部分です。図6のようなレイアウトにします。
図6 UI_DroppableSlotのレイアウト

基本的には、UI_DraggableElementに背景(Border)を付けたようなレイアウトというのが適当な表現かと思います。図6のように、SizeBoxのPaddingを4に、Width、Heightを128にします。

次は図7のように、OnDrop関数を実装します。
図7 OnDropの実装

OnDrop関数はドラッグ中のカーソルが、ドロップ位置であるこのウィジェットにマウスオーバーした後に、ドロップされる(ここでは、マウス左ボタンが離される)と実行されます。処理は、既にUI_DraggableElementを子ウィジェットとして持っているときは、交換処理(図7のコメント部)を行ってから、DropされたウィジェットをAddChildします。この関数の返り値は、ドロップが成功したか否かを表しており、もしこの関数が失敗する(falseを返す)と、図5のOnDragCanceledが実行されます。
これで、UI_DrappableSlotも完成しました。

HUDへの配置

最後に、これらをHUDであるUI_DragDropHUD上に配置します。
その前に、UI_DraggableElementに見た目を与えるために、図8のようにHUD上に配置したDraggableElementのNamedSlotにImageを与えます。NamedSlotは、外部のUserWidgetで使用される場合に、子ウィジェットを追加可能な拡張スロットとして公開されます。簡単にいうと、UserWidgetが形式的に子ウィジェットを持つことができるようになるウィジェットです。
図8 UI_DraggableElementにImageを配置する

同様に、UI_DraggableElementをUI_DroppableSlotのNamedSlotに配置します。
図9 UI_DroppableSlotにUI_DraggableElementを配置する

このようなウィジェットをあと3つ作ります。また、空のUI_DroppableSlotも3つ作成します。そして、これら6つのウィジェットをHorizontalBoxパネルによって整列します。 図10のように配置したら、UI_DragDropHUDは完成です。
図10 UI_DragDropのレイアウト

最後に、レベルブループリントを図11のようにし、実行すれば図1のようなインベントリシステムが出来上がります。
図11 レベルブループリントの実装

おわりに

これを作ろうと思ったきっかけは巷で話題のPUBGがUE4製だと知って、そのインベントリシステムがどう作られているのかが気になったためです。実装はもっとずっと難しいと思っていましたが、実際にはそれほど難しくありませんでした。多くのゲームで使われるシステムを簡単に実装できるのは、UE4ならではの強みだと改めて感じました。


この記事を作成するにあたり、以下の著作物を使用しています。
Game-icons.net (CC By 3.0)

この記事は次のバージョンで作成されました。
Unreal Editor(4.15.1-3348071+++UE4+Release-4.15)

コメント