ARPG開発 003 インベントリと武器装備

マウス&ゲームパッドで操作可能なインベントリを作成しました。
最初は5枠、拡張アイテムを1つ取ると10枠になり、もう1つ取ると15枠になります。
同じアイテムは1つのスロットに入って個数が増えます。
インベントリで武器を選択すると装備でき、装備中の武器を地面に置きます。

UI Navigation 3.0のセットアップ

マウス操作だけのインベントリを作るならチュートリアルは多いのですが、マウス&ゲームパッドになると難しいです。
それを簡単にしてくれるプラグインのUI Navigation 3.0を導入しました。

マーケットプレイスでダウンロード

UI Navigation 3.0はマーケットプレイスで入手できます。無料です。
UI Navigation 3.0:コードプラグイン – UE マーケットプレイス

プラグインの導入

編集 > プラグインからUI Navigationを検索してチェックを入れて再起動。

編集 > プロジェクト設定 > エンジン > ユーザー インターフェースのフォーカス > ファーカスレンダリングルールを「なし(Never)」にしてください。

プレイヤーキャラクターのスケルタルメッシュにgrippointを追加

まずは武器装備の準備。バットと斧を装備できるようにしていきます。

プレイヤーキャラクターのスケルタルメッシュを開く。今回の場合はSKM_Quinnです。

スケルトンツリーのmiddle_01_rで右クリックして「ソケットを追加」し、名前をgrippoint_r_batにしておきます。ここに武器が装着されるようになります。

grippoint_r_batの位置を調整するために、GripPoint_rで右クリックして「プレビューアセットを追加」を選択し、バットのメッシュを選択。
バットができるだけ右手に合うように位置と角度を調整する。これはバットを動かしているのではなく、grippoint_r_batを動かしています。

同じようにgrippoint_r_axeを追加し、プレビューメッシュを斧にして調整しました。

列挙型の作成と編集

バットと斧を分けて管理したいのでEnum(列挙型)を作成します。フォルダ分けは個人の好みで構いません。
コンテンツブラウザで右クリックしてブループリント > 列挙型を選択。
名前はEnum_WeaponTypeにしました。

Enum_WeaponTypeの編集

Enum_WeaponTypeを開く。
「列挙子を追加」を2回クリック。
表示名をBat、Axeにします。

武器クラスの作成

コンテンツブラウザで右クリックからブループリント クラスを選択。親クラスはActor。
名前はBP_WeaponBaseにします。

BP_WeaponBaseの編集

BP_WeaponBaseを開く。
コンポーネントにSkeletal Meshを追加し、名前はWeaponMeshにする。

変数を追加し、名前をWeponTypeにします。その詳細の変数の型にEnum Weapon Typeを指定。先ほど作った列挙型です。

イベントグラフを開き、左メニューから関数を追加。名前をAttachToHandにします。この関数ではプレイヤーの手に武器のMeshを装着する作業をします。

ノードを組む

Attach to Handから線を伸ばしてEnum_WeaponTypeでスイッチを配置。
その中のSelectionは変数のWeaponTypeをGetして繋げる。
Attach Component To Component (Weapon Mesh)を配置し、そのSocket Nameから線を伸ばして変数へ昇格。Location RuleRotation RuleはSnap to Target。Scale RuleはKeep World。ParentはAttach To Handに繋ぐ。繋げなさそうで繋げます。
Enum_WeaponTypeでスイッチの、
・BatからSocket NameをSetして、その中のSocket Nameをgrippoint_r_bat。
・AxeからSocket NameをSetして、その中のSocket Nameをgrippoint_r_axe。
変数のSocket NameをGetしてAttach Component To ComponentSocket Nameに繋げる。

左メニューから関数を追加。名前をSetPlayerRefにします。

ノードを組む

Set Player Ref
を選択して詳細のインプットを追加。ピンタイプをBP_Player(※プレイヤーキャラクターのブループリント)にしてオブジェクト参照。名前はPlayerRefにします。
Set Player RefのPlayer Refから線を伸ばして変数へ昇格。

BP_WeaponBaseの子ブループリントの作成

BP_WeaponBaseの子ブループリントを作成します。
BP_WeaponBaseを選択して右クリック「子ブループリント クラスを作成します」を選択。
2つ作成し、名前はBP_WeaponBatとBP_WeaponAxeにしました。

BP_WeaponBatの編集

WeaponMeshのSkeletal Mesh Assetを選択。
クラスのデフォルト > 詳細でWeapon TypeをBatにする。ここで装備した時にgrippoint_r_batに装備される判断ができます。

BP_WeaponAxeの編集

WeaponMeshのSkeletal Mesh Assetを選択。
クラスのデフォルトの詳細でWeapon TypeをAxeにする。

プレイヤーキャラクターブループリントの編集

プレイヤーキャラクターのブループリントを開く。

ノードを組む

イベント BeginPlay
から続く線にクラスからアクタをスポーンします(Spawn Actor from Class)を配置。これはLevelにバットや斧などのアクターをスポーンさせるものです。
その中のClassBP_WeaponSwordを指定。初期装備となります。
Spawn Transformで右クリックして構造体ピンを分割。
Spawn Transform Locationから線を伸ばしてGet Actor Locationを配置。プレイヤーの近くに武器がスポーンするわけです。瞬時にGripPoint_rに装着されますから、細かな指定は不要。
Collision Handling OverrideAlways Spawn, Ignore Collisionsを選択。コリジョンを無視して装着されます。
Return Valueから線を伸ばして変数へ昇格、名前はSwordRefにします。
セットから線を伸ばしてAttach to Handを配置。先ほど作成した関数を呼ぶわけです。
Attach to Handの中のParentは、コンポーネントからプレイヤーキャラクターのMeshをドラッグ&ドロップで配置して繋げます。
ターゲットセットと繋いでおく。
Sword RefをGet配置して線を伸ばして、関数のSet Player Refを配置。その中のPlayer Refから線を伸ばしてSelf(Get a reference to self)を配置。Attach to Handとも繋ぐ。

この状態でテストプレイすると、プレイヤーキャラクターが武器を持っています。

装備の切り替えの準備

引き続きプレイヤーキャラクターのブループリント。
イベントグラフで関数の追加、名前をEquipWeaponにします。

ノードを組む

イベントのグラフのSpawnActor BP Swordの後のセットからSet Player Refまでをコピーして、EquipWeaponの後にペースト。
セットSword RefCurrentWeaponに名前変更。さらに変数の型をBP_WeaponBase > オブジェクト参照にする。警告が出ますが「変数の型を変更」で問題ありません。

セットCurrent WeaponEquipWeaponに繋ぐ。
EquipWeaponの詳細のインプットをCurrentWeaponからSpawnedWeaponに名前変更。

インタラクトシステム

自分の周囲にあるアイテムに干渉したり、ドアを開けたりするのに使用するインタラクトシステムを作ります。
複雑なノードを組むのが面倒ですが、1度作ってしまえば様々なことに使えます。

ブループリントインターフェースの作成

コンテンツブラウザで右クリックしてブループリント > ブループリントインターフェースを選択。
名前はBPI_Interactにします。
ブループリントインターフェースはブループリントとブループリントの接続を担い、連携が可能。

ブループリントインターフェースの編集

BPI_Interactを開く。
新規関数の画面になっていますので、関数の名前をHasInteractedにします。アイテムやドアなどにインタラクトする関数です。

さらに関数を追加。名前をGetItemDataにします。その詳細のアウトプットを追加して、ItemDataという名前にして、ピンタイプをStruct_ItemDataにします。

プレイヤーブループリントの編集

プレイヤーキャラクターのブループリントを開き、イベントグラフを開く。

BP_Playerのクラス設定 > 詳細 > インターフェース > 実装インターフェースを追加し、BPI_Interactを選択。

アクションイベント > Interactを配置。
関数を追加。名前はFindNearestActorにします。BPI_Interactを実装しているアクターの中で、最も近いアクターを見つける関数になります。

ノードを組む

コンポーネントからCapsule Componentをドラッグ&ドロップで配置。そこから線を伸ばしてGet Overlapping Actorsを配置。
その中のClass FilterはActor、Overlapping Actorsから線を伸ばしてローカル変数へ昇格し、名前をOverlappingActorsにします。セットの左上からFindNearestActorに繋ぐ。
セットの右下から線を伸ばしてIs Valid Indexを配置。
Is Valid Indexから線を伸ばしてブランチを配置。セットとブランチも繋げる。
ブランチのFalseから線を伸ばしてリターンノードを追加。
リターンノードの詳細のアウトプットを追加して、名前をReturnValue。ピンタイプをオブジェクトタイプ > Actor (オブジェクト参照)

ブランチのTrueから線を伸ばしてFor Each Loopを配置。その中のArrayにはOverlappingActorsをGet配置で繋ぐ。
Array Elementから線を伸ばしてDoes Implement Interfaceを配置。その中のInterfaseにはBPI_Interactを指定。Return Valueから線を伸ばしてブランチを配置。ブランチの左からLoop Bodyへ繋ぐ。
ローカル変数を追加し、名前をTempDistance、ピンタイプはFloat。コンパイルしてTempDistanceのデフォルト値を10000.0にする。
For Each LoopArray Elementから線を伸ばしてGet Horizontal Distance toを配置。ターゲットに繋がっている線をOther Actorに繋ぎ直す。繋ぎ直しはCtrlを押しながら。
TempDistanceをGet配置して線を伸ばして>キーを押してより大きい(>)を配置。その左下からGet Horizontal Distance toReturn Valueと繋ぐ。より大きい(>)から線を伸ばしてブランチを配置。それを1つ前のブランチのTrueと繋げる。
TempDistanceをSet配置してブランチのTrueから繋げる。さらにGet Horizontal Distance toReturn Valueからも繋げる。
少し戻ってFor Each LoopArray Indexで右クリックしてローカル変数へ昇格。名前はTempIndexにします。その時に配置されたセットを最後尾に置いて、左上から1つ前のセットと繋げる。
For Each LoopのComplatedから線を伸ばしてリターンノードを追加。
OverlappingActorsをGet配置、線を伸ばして(コピー)を取得、その右からリターンノードと繋ぐ。0のピンにはTempIndexをGet配置して繋ぐ。
FindNearestActorの詳細の純粋にチェックを入れる。

イベントグラフに戻り、再び関数を追加。名前はInteract。

ノードを組む

関数のFindNearestActorを配置。そこから線を伸ばして(?) Is Validを配置し、左上からInteractとも繋ぐ。
FindNearestActorのReturn Valueから再び線を伸ばしてHas Interactedを配置し、左上から(?) Is ValidのIs Validとも繋ぐ。
***この先のノードは後で組みます。

イベントグラフに戻り、関数のInteractを配置して、インプットアクションから繋ぐ。

インベントリーの作成

ウィジェットブループリントの作成と編集

コンテンツブラウザで右クリックしてユーザーインターフェース > ウィジェットブループリントを選択。親クラスはUser Widget。名前はUI_InventorySlotにしました。
同じ手順でもう1つ作成して名前をUI_Inventoryにしました。
UI_InventorySlotがボタンそのもので、UI_Inventoryがそのボタンを並べるものです。

UI_InventorySlotの編集

UI_InventorySlotを開く。
ファイル > ブループリントの親を変更 > UINavComponentを選択。
コンパイル結果で「必須のウィジェット バインディング”*****”が見つかりません」というエラーが複数出ます。必要なものを階層に配置すれば良いだけです。

パレット > パネル > Canvas Panelを階層に配置。
パレット > 一般 > Buttonを階層でCanvas Panelの子に配置。名前をNavButtonに変更します。
パレット > 一般 > Imageを階層でNavButtonの子に配置。名前をSlotImageにします。詳細のIs Variableにチェックを入れておく。

[Canvas Panel]で右クリックして …と置換 > Overlayを選択。

パレット > パネル > Scale Boxを階層の[Overlay]の子に配置。
パレット > 一般 > Textを階層のScale Box*1の子に配置。名前をNumberOfItemsに変更します。詳細のIs Variableにチェックを入れておく。
パレット > パネル > Scale Boxを階層の[Overlay]の子に配置。
パレット > 一般 > Textを階層のScale Box*2の子に配置。名前をItemNameに変更します。詳細のIs Variableにチェックを入れておく。

NavButton(Button)を選択して詳細の変更。
スロット > Horizontal Alignmentの=をクリックして横に広げる。
スロット > Vertical Alignmentの||をクリックして縦に広げる。
アピアランス > Style > Normal > Tintは枠の色です。
アピアランス > Style > Hovered > Tintは選択中の枠の色です。
アピアランス > Style > Pressed > Tintは押した時の枠の色です。
アピアランス > Style > Normal Paddingはボタンの枠の太さです。LeftからBottomまでを1.5にしておきます。
アピアランス > Style > Pressed Paddingはボタンを押したときのボタンの枠の太さです。5.0とか太くしておくとボタンを押した時の反応としてわかりやすいです。

SlotImage(Image)を選択して詳細の変更。
スロット > Horizontal Alignmentの=をクリックして横に広げる。
スロット > Vertical Alignmentの||をクリックして縦に広げる。
スロット > Paddingは上下左右の隙間です。この空白の分だけ下地の色が見えて選択中の枠の色として表示されるわけです。2.0で揃えておきます。

[Scale Box]*1を選択して詳細の変更。
[NumberOfItems]の親側のScale Boxです。
スロット > Paddingは外側の隙間です。10にします。

[NumberOfItems](Text)を選択して詳細の変更。
コンテンツ > Textは空欄にしておきます。
アピアランス > Color and Opacityで数字の色を変えられます。

[ItemName](Text)を選択して詳細の変更。
こちらは表示しません。変数としては使います。
コンテンツ > Textは空欄にしておきます。
動作 > Visibilityを非表示にしておきます。

階層の[UI_InventorySlot]を選択した状態で、詳細の一番上のカテゴリ欄にCommonと入力してEnterキーを押す。

コンパイルしておきます。

UI_Inventoryの編集

UI_Inventoryを開く。
ファイル > ブループリントの親を変更 > UINavWidgetを選択。
こちらはエラーなくコンパイルできます。

パレット > パネル > Canvas Panelを階層に配置。
パレット > 一般 > Textを階層の[Canvas Panel]の子に配置。
パレット > 一般 > Uniform Grid Panelを[Canvas Panel]の子に配置。

[Text]を選択して詳細の変更。
スロット > アンカーを上の真ん中にします(PCの10キーでいう8)。
スロット > 位置Xを0にする。
スロット > 位置Yを180にする。
スロット > AlignmentをX0.5とY0.0にする。
スロット > Size To Contentにチェックを入れて枠を調整。
コンテンツ > TextをInventoryにする。
アピアランス > Font > Sizeを60にする。

[Uniform Grid Panel]を選択して詳細の変更。
スロット > アンカーを中央にします(PCの10キーでいう5)。
スロット > 位置Xを0にする。
スロット > 位置Yを0にする。
スロット > サイズXを700にする。
スロット > サイズYを140にする。
スロット > AlignmentをX0.5とY1.5にする。

パレット > COMMON > UI InventorySlotを階層の[Uniform Grid Panel]の子に配置。

UI InventorySlotを選択して詳細の変更。
スロット > Horizontal Alignmentの=をクリックして横に広げる。
スロット > Vertical Alignmentの||をクリックして縦に広げる。
名前をSlot_0に変更。
Slot_0を4つ複製して、計5にする。その中の1つがSlotという名前なのでSlot_9に変更しておく。階層の並び順も数字順に整えておきましょう。

Slot_1を選択した状態で、
スロット > Columnを1にする。
Slot_2を選択した状態で、
スロット > Columnを2にする。
Slot_3を選択した状態で、
スロット > Columnを3にする。
Slot_4を選択した状態で、
スロット > Columnを4にする。

ここまでがアイテムスロット5枠になります。
拡張アイテムで枠を増やせるように追加で枠を用意します。

パネル > Vertical Boxを階層の[Canvas Panel]の子に配置。
Uniform Grid PanelをVertical Boxの子にして、名前をGrid_0に変更。

[Grid_0]を選択して詳細の変更。
スロット > Sizeのフィルをクリック。

[Vertical Box]を選択して詳細の変更。
スロット > アンカーを中央にします(PCの10キーでいう5)。
スロット > 位置Xを0にする。
スロット > 位置Yを0にする。
スロット > サイズXを700にする。
スロット > サイズYをにする。※3段15枠の場合
スロット > AlignmentをX0.5とY0.5にする。

Grid_0を2つ複製して名前をGrid_1とGrid_2にします。
Grid_1とGrid_2のSlotの名前を全て変更します。ボタンの配置場所と名前を合わせる必要があるため、右下のボタンがSlot_14になるように全て名前変更が必要です。

Grid_1とGrid_2を拡張枠にしますから詳細 > 動作 > Visibilityを非表示にする。
Grid_0、Grid_1、Grid_2の詳細 > Is Variableにチェックを入れて変数にします。

プレイヤーコントローラーの作成と編集

コンテンツブラウザで右クリックしてブループリント クラスを選択。
親クラスはPlayer Controller。
名前はBP_PlayerControllerにしました。

BP_PlayerControllerの編集

BP_PlayerControllerを開く。
ファイル > ブループリントの親を変更 > UINavControllerを選択。
エラーなくコンパイルできます。

クラス設定 > 詳細 > インターフェース > 継承インターフェースにUINavPCReceiverが入っているのを確認。もし入っていなければ実装インターフェースを追加し、UINavPCReceiverを選択。

プロジェクト設定

編集 > プロジェクト設定 > マップ&モード > Default Modes > 選択したゲームモード > Player Controller ClassをBP_PlayerControllerに変更してください。

この状態でテストプレイをすると設定したボタンでインベントリを開きます。マウスでもゲームパッドでもボタンの選択が可能です。
枠の色を変えていないと選択できているかどうかわかりにくいです。

構造体の作成と編集

右クリックしてブループリント > 構造体を選択。
2つ作成し、名前はStruct_ItemDataとStruct_WeaponDataにします。

Struct_ItemDataの編集

Struct_ItemDataを開き、最初からあるMemberVar_0をSlotImageに名前変更。ピンタイプはTexture 2D (オブジェクト)にする。
※構造体が表示されていない時は上メニューのウィンドウから構造体にチェックを入れる。
+変数を追加をクリックして、
回復アイテムとなるHealingValueをFloatで作成。
インベントリ枠拡張アイテムとなるAddInventorySlotをBooleanで作成。
アイテム個数となるNumberOsItemsをIntegerで作成。デフォルト値は1にします。
1回使用あたりの消費個数となるItemUsageをIntegerで作成。デフォルト値は1にします。
名前となるItemNameをTextで作成。
装備品かの判定をするEquipment?をBooleanで作成。
装備武器と連動させたいのでWeaponClassをBP Weapon Base (クラス)で作成。

Struct_WeaponDataの編集

アイテムスロットで武器を装備した時、それまで装備中だった武器を捨てるために構造体を使います。武器はBP_Weaponを装備しますが、落ちている状態ではBP_DropWeaponを使いますから、構造体を経由して切り替えます。

変数は1つのみ。
名前をDropItemClassにしてピンタイプはBP Drop Item Base (クラス)です。

BP_WeaponBaseにStruct_WeaponDataを追加

BP_WeaponBaseの変数を追加。名前をWeaponDataにして、ピンタイプをStruct Weapon Dataにします。

インターフェースを作成

Blueprintsフォルダで右クリックして、ブループリント > ブループリントインターフェース。名前をBPI_Playerにします。

BPI_Playerの編集

BPI_Playerを開くと新規関数の画面になりますので名前はSetItemDataにします。詳細のインプットを追加して、名前をItemData、ピンタイプはStruct_ItemData。

BPI_Interactの編集

既に作成済のBPI_Interactにも関数を追加します。
名前をGetItemDataにします。その詳細のアウトプットを追加して、ItemDataという名前にして、ピンタイプをStruct_ItemDataにします。

アイテムの作成

任意のフォルダで右クリックしてブループリント クラス、親クラスはActor。名前はBP_DropItemBaseにします。

BP_DropItemBaseの編集

コンポーネントにSkeltal Meshを追加し、名前をDropItemMasnにしました。
さらにSphere Collisionも追加し、名前をDropItemCollisionにしました。
Static MeshからSkeltal Meshへの変換は難しくないですから、基本的にSkeltal Meshを使います。

クラス設定 > 詳細 > インターフェース > 実装インターフェースを追加してBPI_Interactを配置。

左メニューのインターフェースにあるGet Item Dataをダブルクリックで開く。
リターンノードItem Data変数へ昇格

コンパイルしておきましょう。

子ブループリントの作成

BP_DropItemBaseで右クリックして子ブループリント クラスを作成します

回復アイテムの作成

BP_DropItemBaseから子ブループリントを作成し、名前はBP_DropItemHealingにします。
BP_DropItemHealingの詳細、
■Item Data
SlotImageにスロットイメージをTexture 2Dから選択。
HealingValueに回復量を入力。
NumberOfItemsは1。
ItemUsageは1。
ItemNameはPotion。
コンポーネントのDropItemMasnを選択し、詳細のスタティックメッシュ > Skeletal Mesh Assetを選択。
ビューポートでSphere (Collision)のスケールを大きくして、プレイヤーが接触できるようにしてください。

スロット枠拡張アイテムの作成

BP_DropItemBaseから子ブループリントを作成し、名前はBP_DropItemAddSlotにします。
BP_DropItemAddSlotの詳細
■Item Data
SlotImageにスロットイメージを選択。
AddInventorySlotにチェックを入れる。
NumberOfItemsは1。
ItemUsageは1。
ItemNameはItemAddSlot。

コンポーネントのDropItemMasnを選択し、詳細のスタティックメッシュ > Skeletal Mesh Assetを選択。
ビューポートでSphere (Collision)のスケールを大きくして、プレイヤーが接触できるようにしてください。

Levelに配置する武器 Batの作成

BP_DropItemBaseから子ブループリントを作成し、名前はBP_DropWeaponBatにします。
BP_DropWeaponBatの詳細
■Item Data
SlotImageにスロットイメージを選択。
NumberOfItemsは1。
ItemUsageは1。
ItemNameはDropBat。
Equipment?にチェックを入れる。
WeaponClassはBP_WeaponBatを選択。

BP_DropWeaponBatとBP_WeaponBatを分けておかないと、自分の持っている武器もインタラクトボタンで検知してしまいます。

BP_DropWeaponAxeも同じように作成する。

SlotImageに使えるアイコンはフリーの配布サイトもあります。
icooon-mono
Game-icons.net ※クレジット表記必要
ダウンロードしたらコンテンツブラウザにドラッグ&ドロップするだけです。

プレイヤーブループリントの編集

ノードを組む

イベント BeginPlayから続く最後のノードから線を伸ばしてウィジェットを作成を配置。ClassをUI_Inventoryにする。Return Valueから線を伸ばして変数へ昇格、名前をInventoryWidgetにします。

UI_InventorySlotの編集

右上の「グラフ」をクリックしてイベントグラフへ。
デフォルトのノードは全削除して構いません。

スロットが空になったことを知らせる処理を作ります。
変数を+して追加し、名前をSlotNumberにしてピンタイプをIntegerにし、目玉アイコンを開けておきます。
左メニューのイベントディスパッチャーを+して追加し、名前をClearSlotにします。
ClearSlotの詳細 > インプットを+して追加し、名前をSlotNumberにしてピンタイプをIntegerにします。

ノードを組む カスタムイベント SetSlot

カスタムイベントを追加して、名前をSetSlotにします。
SetSlotを選択して詳細のインプットを3つ追加。
・名前をSlotImageにしてピンタイプをTexture 2D(オブジェクト参照)にします。
・名前をItemNameにしてピンタイプをTextにします。
・名前をNumberOfItemsにしてピンタイプをIntegerにします。
SetSlotから線を伸ばして「状況に合わせた表示」のチェックを外してからSet Brush from Texture (ターゲットはImage)を配置。その中のTextureSlot Imageを繋ぐ。
変数のSlotImageをGetしてターゲットと繋ぐ。
変数ItemNameをGetして線を伸ばしてSetText (Text)を配置し、Set Brush from Textureから線を伸ばして繋ぐ。In TextにはSetSlotItem Nameを繋ぐ。
変数NumberOfItemsをGetして線を伸ばしてSetText (Text)を配置し、先に配置したSetText (Text)から線を伸ばして繋ぐ。In TextにはSetSlotNumber Of Itemsを繋ぐ。
そこから線を伸ばしてSet Visibilityを配置。その中のIn Visibilityは表示。ターゲットから線を伸ばしてNavButtonを取得。さらに変数ItemNameNumberOfItemsをGetしてターゲットに繋げる。
最初に武器装備の関数を作ります。名前をEquipWeaponsにする。
ノードを組む

Equip Weaponsから線を伸ばしてクラスからアクタをスポーンしますを配置。Spawn Transformで右クリックして構造体ピンを分割。Collision Handling OverrideはTry To Adjust Location, But Always Spawnにします。
Cast To Bp Playerを配置し、ObjectにはGet Player Characterを配置して繋ぐ。
As BP Playerから線を伸ばしてGet Actor Forward Vectorを配置して線を伸ばして乗算する(*)を配置、左下のピンはFloat(単精度)にして150.0と入力。
As BP Playerから線を伸ばしてGet Actor Locationを配置して加算する(+)を配置し、左下は乗算する(*)から綱ぐ。右からSpawn Transform Locationに繋ぐ。
As BP Playerから線を伸ばしてCurrent Weaponを取得Weapon Dataを取得Struct_WeaponDataを分解Classに繋ぐ。
As BP Playerから線を伸ばしてGet Actor Rotationを配置し、Spawn Transform Rotationに繋ぐ。
↑ここまでは装備中の武器をLevel上に落とす処理です。
スポーンアクタから線を伸ばして、再びクラスからアクタをスポーンしますを配置。Collision Handling OverrideはAlways Spawn, Ignore Collisionsにします。
変数Item DataをGetしてStruct_ItemDataを分解Classに繋ぐ。
As BP Playerから線を伸ばしてGet Actor Transformを配置してSpawn Transformに繋ぐ。
スポーンアクタから線を伸ばしてDestroy ActorEquip Weaponリターンノードを配置。
Destroy ActorのターゲットはCurrent Weapon
Equip WeaponのターゲットはAs BP PlayerSpawned WeapnスポーンアクタReturn Valueも繋ぐ。
デザイナーでNavButtonを選択し、詳細 > イベントのOn Clickedを+してOn Clicked (NavButton)を配置する。
ノードを組む On Clicked (NavButton)
【True側】アイテム個数が1以上

変数Item DataをGetして、右クリックから構造体ピンを分割。その中のItem Data Number Of Itemsから線を伸ばして減算する(-)を配置。その左下はItem Data Item Usageを繋ぐ。所持アイテムの個数から、1回の使用で消費する数を引くわけです。右から線を伸ばして以上(>=)を配置。値は1にする。
さらに線を伸ばしてブランチを配置。On Clicked (NavButton)からブランチに繋ぐ。
変数NumberOfItemsをGetして線を伸ばしてSetText (Text)を配置し、ブランチのTrueから繋ぐ。In Text減算する(-)の右上から繋ぐ。
変数ItemDataをGetして線を伸ばしてStruct_ItemData にメンバを設定を配置。SetText (Text)から繋ぐ。
Struct_ItemData にメンバを設定を右クリックしてすべての構造体ピンをリストア減算する(-)の右上からNumber Of Itemsに繋ぐ。そのNumber Of Itemsで右クリックして他のピンをすべて除去。Slot Imageが消えない場合、Slot Imageで右クリックしてデフォルト値にリセット。
線を伸ばしてSet Item Data (メッセージ)を配置。ターゲットはGet Player CharacterItem DataStruct Outと繋げる。
Set Item Dataから線を伸ばしてブランチを配置。
変数のItem DataをGetして線を伸ばしてStruct_ItemData を分解し、その中のEquipment?から線を伸ばしてブランチConditionに繋げる。
ブランチのTrue側から線を伸ばして関数のEquip Weaponsを配置。
ブランチのFalse側から線を伸ばしてブランチを配置。
Struct_ItemData を分解の中のAdd Inventory Slotから線を伸ばしてブランチConditionに繋げる。
左メニューのイベントディスパッチャーを追加して、名前をAddNewSlotにします。AddNewSlotをドラッグ&ドロップで呼び出す。ブランチのTrueから繋げる。
【Fales側】アイテム個数が0

Struct_ItemData にメンバを設定Set Item Data (メッセージ)まではTrueと同じ。
そこから線を伸ばしてブランチを配置。
変数のItem DataをGetして線を伸ばしてStruct_ItemData を分解し、その中のEquipment?から線を伸ばしてブランチConditionに繋げる。
ブランチのTrue側から線を伸ばして関数のEquip Weaponsを配置。
ブランチのFalse側から線を伸ばしてブランチを配置。
Struct_ItemData を分解の中のAdd Inventory Slotから線を伸ばしてブランチConditionに繋げる。ブランチのTrue側からAddNewSlotに繋げる。
Set Brush from Texturesを配置。ターゲットに変数のSlot ImageをGetして繋げる。ここでTextureを入れないことでアイテム画像を消します。
Set Visibilityを配置。ターゲットには変数NumberOfItemsItemNameを繋ぎ、In Visibilityを非表示にする。アイテム個数と名前を消します。
その先にItemDataをセットし、その枠のデータをリセットします。
セットからClear Slotの呼び出しSet Item Dataを繋ぐ。
Clear Slotの呼び出しSlot NumberをGetして繋ぐ、Set Item DataItem DataItem DataをGetして繋ぐ。ターゲットはGet Player Character
ノードを組む SetItemData

カスタムイベントを追加し、名前をSetItemDataにします。そのインプットを+して、名前をItemData、ピンタイプはStruct_ItemDataにします。
ItemDataから線を伸ばして変数へ昇格セットから線を伸ばして関数Set Slot Imageを配置。
セットの右下から線を伸ばしてをStruct_ItemData を分解し、Slot Image同士とNumberOfItems同士を繋げる。

UI_Inventoryの編集

右上の「デザイナー」をクリックしてデザイナーを開く。
階層のSlot_0からSlot_14に詳細 > デフォルト > Slot Numberが追加されています。Slot_0はSlot Number 0、Slot_1はSlot Number 1という具合にSlot_14まで入力します。

右上の「グラフ」をクリックしてイベントグラフへ。
デフォルトのノードは全削除して構いません。

ノードを組む

イベント On Initializedを配置。
変数のSlot_0からSlot_9までを全てGetする。Ctrlを押しながらドラッグ&ドロップすると自動でGetになります。
Slot_0から線を伸ばして配列を作成(Make Array)を配置。ピンを追加をクリックして14本のピンを追加し、Slot_1からSlot_19までを繋ぐ。
Arrayから線を伸ばして変数へ昇格。名前はSlotArrayにします。
セットの左上とイベント On Initializedを繋ぐ。
セットから線を伸ばしてFor Each Loopを配置。Arrayも繋ぐ。
For Each LoopのLoop Bodyから線を伸ばしてブランチを配置。
For Each LoopArray Indexから線を伸ばして以下(<=)を配置。この中の値は最初に使用させたいスロット番号を入れます。4にすると5枠、9にすると10枠が最初から使用可能。今回は4です。線を伸ばしてブランチConditionと繋ぐ。
変数を追加して、名前をSlotStateにします。ピンタイプをUI Inventory Slot(オブジェクト参照)、その隣のコンテナをマップにします。さらに値タイプが出現するのでBooleanにします。このBooleanがTrueならそのスロットは使用中ということにしていきます。

変数のSlotStateをGetして線を伸ばしてAddを配置。Trueから繋ぐ。Array Elementアセットを選択も繋ぐ。
□にチェックは入れません。Array Elementで順に呼ばれていって、全てにFalseが入ります。
SlotStateAddを複製してFalseからも繋ぎ、こちらはチェックを入れる。Array Elementアセットを選択も繋ぐ。
True側のADDから線を伸ばして「状況に合わせた表示」のチェックを外してからClear Slot にイベントをバインド(Bind Event to Clear Slot)を配置。次からは「状況に合わせた表示」のチェックを入れる。False側のADDもClear Slot にイベントをバインドに繋ぐ。
ターゲットFor Each LoopArray Elementと繋ぐ。
イベントから線を伸ばしてカスタムイベントを追加、名前はClearSlotにします。
変数のSlotArrayをGetして線を伸ばして(コピー)を取得(GET)を配置。その中の0ClearSlotSlot Numberを繋ぐ。
変数のSlotStateをGetして線を伸ばしてAddを配置。ClearSlot(コピー)を取得からそれぞれ繋ぐ。は空欄、つまりクリックされたスロット番号をFalseにする処理です。
Clear Slot にイベントをバインドから線を伸ばしてAdd New Slotにイベントをバインドを配置。ターゲットはFor Each LoopArray Elementと繋ぐ。
そのイベントから線を伸ばしてカスタムイベントを追加、名前はAdd New Slotにします。
変数を追加。名前はNewSlotCountでピンタイプはInteger。型がマップになっているなら単一にする。コンパイルしてデフォルト値を1にしておきます。
Add New Slotから線を伸ばしてFor Loopを配置。
NewSlotCountをGetして線を伸ばして乗算する(*)を配置。その中の値は5にする。これをFor LoopFirst Indexに繋ぐ。
さらに乗算する(*)の右上から線を伸ばして加算する(+)を配置。値は4にする。これをLast Indexに繋ぐ。New Slot Countの初期値は1なので1×5+4で9になります。Slot_0もありますから、初期値5だったのが拡張アイテム1回取得で計10枠になる計算。
変数のSlotArrayをGetして線を伸ばして(コピー)を取得(GET)を配置。For LoopのIndexから繋ぐ。
変数のSlotStateをGetして線の伸ばしてAddを配置。For LoopのLoop Bodyから繋ぐ。アセットを選択は(コピー)を取得(GET)と繋ぐ。
NewSlotCountをGetして線を伸ばして整数型でスイッチ(Switch on Int)を配置。For LoopのComplatedから繋ぐ。
整数型でスイッチのピンを3つ追加。
変数Grid_1をGetして線を伸ばしてSet Visibilityを配置。In Visibilityは表示。整数型でスイッチのピン1から繋ぐ。
変数Grid_2をGetして線を伸ばしてSet Visibilityを配置。In Visibilityは表示。整数型でスイッチのピン2から繋ぐ。
NewSlotCountをGetして線を伸ばして++キーでIncrement Intを配置。両方のSet Visibilityから繋ぐ。

空のアイテムスロットNoを取得する関数を作ります。関数を追加して名前はGetAvailableSlotNumberにします。Availableは「利用可能な」という意味。

ノードを組む

変数のSlotStateをGetし、線を伸ばしてValuesを配置。ValuesはSlotStateのBooleanの値を取得するノードです。
Get Available Slot NumberからValuesに繋げる。
Valuesの右下から線を伸ばしてFind Itemを配置。これはValuesで検索したアイテムコンテナの中にFales(空)があればそれを取得するノードです。全てTrue(使用中)なら-1を受け取る。
リターンノードを追加して、それぞれ繋げる。
リターンノードのアウトプットの名前をSlotNumberにします。
Get Available Slot Numberを選択して詳細の純粋にチェックを入れます。

Slotに画像を割り当てる関数を作ります。関数を追加して名前はUpdateInventorySlotにします。

ノードを組む

関数のGet Available Slot NumberをGetして線を伸ばして!キーを押して等しくない(!=)を配置。その中の値を-1にします。-1とはTrueであり、全てのSlotが使用中ということです。
右から線を伸ばしてブランチを配置。Update Inventory Slotからも繋ぐ。
変数のSlotArrayをGetして線を伸ばして(コピー)を取得(GET)を配置。その中の値はGet Available Slot NumberのSlot Numberから線を繋ぐ。空いているSlotの一番小さい番号を取得しました。
GETから線を伸ばしてSet Item Dataを配置。その左上からTrueに繋ぐ。
空いてた枠を使用しますから、その枠をTrueにする必要があります。Set Item Dataの中のItem Dataから線を伸ばしてUpdate Inventory Slotに繋ぐ。
変数のSlotStateをGetして線を伸ばしてAddを配置し、Set Item Dataから繋げる。
Addアセットを選択(コピー)を取得(GET)から繋げる。
Addにチェックを入れることでTrueになります。
Addから線を伸ばしてリターンノードを追加。
リターンノードの詳細のアウトプットを追加し、名前をStored、ピンタイプはBooleanにする。格納済という意味です。
リターンノードのStoredにチェックを入れる。
リターンノードを複製してStoredにチェックを外し、ブランチのFalseから繋げる。格納できなかったということです。
同じアイテムなら数を増やす処理をします。
関数を追加して、名前をFindSameItemSlotにします。
ノードを組む
Find Same Item Slotのインプットを追加して、名前はItemData、ピンタイプはStruct Item Data。
変数SlotArrayをGetして線を伸ばしてFor Each Loop with Breakを配置。Find Same Item Slotからも繋ぐ。For Each Loop with Breakの中のArray Elemenから線を伸ばしてItem Data を取得を配置。そのItem Dataで右クリックして構造体を分割させる。こちらは既に所有しているアイテムデータ。
Find Same Item SlotItem Dataから線を伸ばしてStruct_ItemData を分解を配置。vで広げておく。こちらは入手したアイテムデータ。
Array Element側のItem Data Item Nemaから線を伸ばして==キーを押してEqual, Case Insensitive (Text)を配置。左下にはStruct_ItemData を分解側のItem Nameを繋ぐ。右から線を伸ばしてブランチを配置。For Each Loop with Breakからブランチに繋ぐ。
Find Same Item Slotの線を伸ばしてStruct_ItemData にメンバを設定を配置。これをブランチのTrueから繋ぐ。
Struct_ItemData にメンバを設定で右クリックしてすべての構造体ピンをリストアを選択。
Array Element側のItem Data Number Of Itemsから線を伸ばして加算する(+)を配置。その左下はStruct_ItemData を分解側のNumber Of Itemsを繋ぐ。右から線を伸ばしてStruct_ItemData にメンバを設定のNumber Of Itemsに繋ぐ。そしたらそのNumber Of Itemsで右クリックして他のピンをすべて除去を選択。Slot Imageが消えない場合、Slot Imageで右クリックしてデフォルト値にリセット。
For Each Loop with BreakかのArray Elementから線を伸ばして関数Set Item Dataを配置。Struct_ItemData にメンバを設定から繋げる。
ローカル変数IsSameItem_Local?(Boolean)を追加。SetしてSet Item Dataの後に繋げて枠にチェックを入れる。同じ名前のアイテムがあった時、この変数にチェックが入る。右上から線を伸ばしてFor Each Loop with BreakのBreakに繋げる。ループ終了させるわけです。
For Each Loop with BreakのComletedから線を伸ばしてリターンノードを追加。IsSameItem_Local?をGetしてリターンノードに繋げてアウトプットになります。アウトプットの変数の名前はIsSameItem?に変えておきます。
再びIsSameItem_Local?をSetしてブランチのFalseから繋げて、枠のチェックは入れない。

関数UpdateInventorySlotをダブルクリックで開く。

ノードを組む

関数のFindSameItemSlotを配置し、Update Inventory Slotから2本の線を伸ばして繋ぐ。
Find Same Item SlotIs Same Item?から線を伸ばしてブランチを配置。Trueからリターンノード(Storedにチェック入り)を複製して繋ぐ。Falseから次のブランチに繋げる。

プレイヤーキャラクターのブループリントを編集

BP_Playerのクラス設定 > 詳細 > インターフェース > 実装インターフェースを追加し、BPI_Playerを選択。

ノードを組む

左メニューの関数Interactをダブルクリックして続き。
Get Item DataUpdate Inventory Slotブランチ → TrueからDestroy Actorを配置。
Update Inventory SlotターゲットInventory Widget
これはインタラクトしたアイテムのデータを取得し、Level上から削除しています。
ノードを組む

イベント Set Item Dataを配置。Item Dataから線を伸ばして変数へ昇格
セットの右下から線を伸ばしてStruct_ItemData を分解を配置。その中のHealing Valueから線を伸ばして加算する(+)を配置。
加算する(+)の左下には現在のHpの変数をGetして繋ぐ。
その先に現在のHpの変数をSetして繋げる。
ここから先にはHpBarを更新させるノードを繋げる予定です。

BP_PlayerControllerの編集。

イベントグラフを開く。

ノードを組む

インベントリを開くボタンを自分で決めてインプットアクションを配置してください。コンテンツ > エンジン > Plugins > UI Navigation コンテンツ > InputにIC_UINavがあり、そこの初期設定ではEscキー、Tabキー、ゲームパッドBボタンがリターンボタンになっています。これと同じボタンにすると不具合があります。
インプットアクションのPressedもしくはStartedから線を伸ばしてCast To BP_Playerを配置。ObjectにはGet Player Characterを繋ぐ。As BP Playerから線を伸ばしてInventoryWidgetを取得。Cast To BP_Playerから線を伸ばしてFlip Flopを配置。
【A側】
「状況に合わせた表示」のチェックを外してからAdd to Viewportを配置。ターゲットにはInventoryWidgetを繋げる。
【B側】
「状況に合わせた表示」のチェックを外してからRemove from Parentを配置。ターゲットにはInventoryWidgetを繋げる。
イベント On Root Widget Addedを配置して線を伸ばしてSet Show Mouse Cursorを設置し、枠のチェックを入れる。
線を伸ばしてSet Ignore Look Inputを配置し、枠のチェックを入れる。
線を伸ばしてSet Ignore Move Inputを配置し、枠のチェックを入れる。
イベントOn Root Widget Removedを配置して線を伸ばしてSet Show Mouse Cursorを設置し、枠のチェックを外す。
線を伸ばしてSet Ignore Look Inputを配置し、枠のチェックを外す。
線を伸ばしてSet Ignore Move Inputを配置し、枠のチェックを外す。

参考サイト

【UE4】アイテムをインベントリに格納する方法【逆引きUE】 – YouTube

インベントリは難しい

インベントリは超難関でした。
まずマウス&ゲームパッドの両対応にするのが難しかったですし、インベントリから武器を装備する処理も悩みました。
そもそもインベントリやアイテムや装備の仕様をちゃんと決めていないと形になりません。真似だけしていればいいチュートリアル教材とは大きな違いがあり、高い壁でした。しかし、ここで悩んだことは大きな成長になったと思います。ようやく、見ながら作業を真似るだけの学習から脱した気がします。
まだまだ理解が浅くて複雑で、こうやって自分用のメモとして残しておかないと再現もできないです。
インベントリやアイコンのデザインにもこだわりたいところではありますが、まずは浅く形にしてゲーム全体を完成させることが第一です。要領よく完成を目指したいです。

コメント

タイトルとURLをコピーしました