複数の武器を持ち替え可能なシステム

剣、斧、槍を拾って持ち替えるシステムです。
もちろん銃の持ち替えにも応用できます。

マーケットプレイス

Free Fantasy Weapon Sample Packをプロジェクトに追加します。

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

今回はThirdPersonTemplateのBP_ThirdPersonCharacterをコピーして、名前をBP_Playerにしてプレイヤーキャラクターとして使っています。
そのSkeletal Mesh AssetをSKM_Quinnにしています。

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

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

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

列挙型の作成と編集

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

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

武器クラスの作成

Weaponフォルダを作成。
右クリックからブループリント クラスを選択。親クラスはActor。
名前はBP_WeaponBaseにします。

BP_WeaponBaseの編集

BP_WeaponBaseを開く。
コンポーネントにSkeletal Meshを追加し、名前はWeaponMeshにする。
変数を追加し、名前をWeponTypeにします。その詳細の変数の型にEnum Weapon Typeを指定。先ほど作った列挙型です。

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

ノードを組む

Attach To Hand
から線を伸ばしてAttach Component To Component (Weapon Mesh)を配置。
その中のParentAttach To Handに繋ぐ。繋げなさそうで繋げます。
Socket NameはGripPoint_rと入力。最初に設定しておいたGripPoint_rに武器がくっつくわけです。
Location RuleRotation RuleはSnap to Target。
Scale RuleはKeep World。

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

ノードを組む

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

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

BP_WeaponBaseの子ブループリントを作成します。
BP_WeaponBaseを選択して右クリック「子ブループリント クラスを作成します」を選択。
名前はBP_WeaponSwordにしました。
同じ要領でBP_WeaponAxeとBP_WeaponSpearも作成します。

BP_WeaponSwordの編集

WeaponMeshのSkeletal Mesh AssetをSK_Swordにします。
クラスのデフォルトの詳細でWeapon TypeをSwordにする。

BP_WeaponAxeの編集

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

BP_WeaponSpearの編集

WeaponMeshのSkeletal Mesh AssetをSK_Spearにします。
クラスのデフォルトの詳細でWeapon TypeをSpearにする。

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

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

ノードを組む

イベント BeginPlay
から続く線にクラスからアクタをスポーンします(Spawn Actor from Class)を配置。
その中の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にします。アイテムやドアなどにインタラクトする関数です。

プロジェクト設定

編集 > プロジェクト設定 > エンジン > インプットを開く。
バインディング > アクションマッピングを+で追加。名前はInteract。
入力ボタンは好きなように決めてください。今回はゲームパッドBにしました。

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

プレイヤーキャラクターのブループリントを開き、イベントグラフを開く。
関数を追加。名前はFindNearestActorにします。最も近いアクターを見つける関数になります。

ノードを組む

コンポーネントから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を配置してPressedと繋ぐ。

武器持ち替えシステム

Weaponフォルダで右クリックしてブループリント クラスを選択、親はActor。
名前はBP_DropWeaponBaseにします。

BP_DropWeaponBaseの編集

BP_DropWeaponBaseを開く。
コンポーネントにSkaletal Meshを追加し、名前をDisplayMeshにします。
DisplayMeshの子としてBox Collisionを追加し、名前をOverlapTriggerにします。
クラス設定 > 詳細のインターフェース > 実装インターフェースに追加し、BPI_Interactを選択。

ノードを組む

コンパイルしてからイベントグラフで右クリックしてイベント Has Interactedを配置。そこから線を伸ばしてクラスからアクタをスポーンします(Spawn Actor NONE)を配置。
その中のClassから線を伸ばして変数へ昇格。名前はSpawnWeaponClass。
Spawn Transformから線を伸ばしてGet Actor Transformを配置。
Collision Handling OverrideはAlways Spawn, Ignore Collisions。
Return Valueから線を伸ばしてCast To BP_WeaponBaseを配置して右クリックメニューから純粋キャストに変更しておきます。
ノードを組む

コンポーネントのOverlapTriggerを選択して右クリックしてイベントを追加 > On Component Begin Overlapを追加。
同じ要領で今度はイベントを追加 > On Component End Overlapを追加。
On Component Begin OverlapOther Actorから線を伸ばして変数へ昇格。名前をOverlapActorにします。
再びOther Actorから線を伸ばして==キーを押して等しい(==)を配置。その左下から線を伸ばしてGet Player Pawnを配置。等しい(==)から線を伸ばしてブランチを配置し、セットの前に位置変更。線の付け替えでOther Actorも忘れないようにしましょう。
ノードを組む

OverlapActorをGet配置して線を伸ばしてCast To BP_Playerを配置。プレイヤーキャラクターのブループリントですから人によって名前が違うかもしれません。スポーンアクタの右上から繋げる。
Cast To BP_PlayerAs BP Playerから線を伸ばしてGet Current Weaponを配置。そこから線を伸ばしてDestroy Actorを配置し、Cast To BP_Playerから繋げる。
再びCast To BP_PlayerAs BP Playerから線を伸ばして関数のEquip Weaponを呼び出す。
Equip Weaponの中のSpawned WeaponCast To BP_WeaponBaseAs BP Weapon Baseと繋ぐ。
Destroy ActorからEquip Weaponへも繋ぐ。
ついでにクラス設定 > Generate Abstract Classにチェックを入れておきましょう。Baseを置かなくなります。

子ブループリントの作成

BP_DropWeaponBaseから子ブループリントクラスを作成し、名前をBP_DropWeaponSwordにします。
同じ要領でBP_DropWeaponAxeとBP_DropWeaponSpearも作成。

BP_DropWeaponSwordの編集

BP_DropWeaponSwordを開き、DisplayMeshのSkeletal Mesh AssetをSK_Swordにする。
クラスのデフォルトの詳細のSpawn Weapon ClassをBP_WeponSwordにする。

同じようにBP_DropWeaponAxeとBP_DropWeaponSpearもSkeletal Mesh AssetとSpawn Weapon Classの設定をする。

レベルにBP_DropWeaponSword、BP_DropWeaponAxe、BP_DropWeaponSpearを配置してテストプレイ。近づいてインタラクトすれば装備できます。

武器よって持ち方がズレる問題の修正

GripPoint_rはSwordに合わせていますから、AxeやSpearを装備すると持ち方がおかしくなっています。

プレイヤーキャラクターのスケルタルメッシュを開き、スケルトンツリーのmiddle_01_rで右クリックして「ソケットを追加」します。
新たにGripPoint_r_AxeとGripPoint_r_Spearというソケットを追加しました。
右クリックして「プレビューアセットを追加」を選択し、それぞれAxeとSpearを表示させて調整します。

BP_WeaponBaseの編集

BP_WeaponBaseを開き、左メニューの関数のAttach to Handをダブルクリックで開く。

ノードを組む

Attach to Handから線を伸ばしてEnum_WeaponTypeでスイッチを配置。
その中のSelectionは変数のWeaponTypeをGetして繋げる。
Attach Component To ComponentSocket Nameから線を伸ばして変数へ昇格。
Enum_WeaponTypeでスイッチの、
・SwordからSocket NameをSetして、その中のSocket NameをGripPoint_r。
・AxeからSocket NameをSetして、その中のSocket NameをGripPoint_r_Axe。
・SpearからSocket NameをSetして、その中のSocket NameをGripPoint_r_Spear。
変数のSocket NameをGetしてAttach Component To ComponentSocket Nameに繋げる。
武器を列挙型(Enum)で管理していれば、こうやってEnum_WeaponTypeでスイッチを使って切り替えることができます
追加でPlay Sound at Locationを追加して、その中のSoundで音を指定すれば、装備を切り替える時に音が鳴ります。
LocationにはGet Actor Locationを繋げます。
Enum_WeaponTypeでスイッチの後であれば、武器によって音を変えられます。
テストプレイは、BP_DropWeapon_AxeとBP_DropWeapon_SwordとBP_DropWeapon_Spearをレベルに配置して、近づいてアクションボタンを押してみます。

コメント

  1. 初心者 より:

    はじめまして、最近UE5を触り始めた初心者です。ちょうど武器の持ち替えの実装がわからなかったところなので大変勉強になります。ありがとうございます!
    ところで、上記の方法を試してみたのですが、インタラクトすると手持ちの武器が消えるだけで持ち替えることができませんでした。
    On Component End Overlapに関して説明がなかったのでもしかしたらそれかなと思ったのですが、関係ないのでしょうか?
    お手すきの際にお返事を頂ければ幸いです。

  2. 初心者 より:

    すみません、先ほど質問したものです。自分のミスに気付いて、手持ちの武器だけが消えてしまう現象は解決することができました。
    しかし、また問題に直面してしまいました。
    武器をインタラクトで交換する際、交換する前の武器がその場に残ってしまうのですが、これは記事通りにできているのでしょうか?
    度々すみません。

    • Hummerkey より:

      古い記事でしたので自分で見ながらやってみました。*UE5.4.2

      拾ったはずの武器が地面に落ちたままという意味でしたら、それで正常な動作です。
      インタラクトされた時に配置されている武器を削除するノードは組んでいません。

      拾われた武器をレベルから削除したい場合、BP_DropWeaponBaseのEquip Weaponの後にDestroy Actorを配置し、そのターゲットをSelfにすれば良いです。
      拾われた時に配置してあったアクター自身が削除されることになります。

      プレイヤーの手に前の武器が残っているという意味だとしたら、BP_DropWeaponBaseのDestroy Actor(ターゲット:Current Weapon)が機能していないことになりますので、その周辺にミスがあるはずです。
      これは武器を拾った時に現在装備中の武器を削除するノードです。
      プレイヤーキャラクターのブループリントを見直して探すと問題が見つかると思います。

  3. 初心者 より:

    先ほど気が付きました!ご丁寧にご返信くださりありがとうございます!
    わざわざ動作確認までしていただいたようで恐縮です。

    正常な動作とのことで安心しました。自分のやりたかったこととは少し違ったのですが、大変勉強になります!

    Hummerkeyさんの記事など参考にしながらいろいろ試行錯誤してみようと思います。
    ネット上にこのような丁寧な解説を残してくださり、本当にありがとうございます。

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