セーブとロード

メインメニューとセッティングメニューが上手くいったので、セーブとロードもメインメニューと連動させました。

Struct_PlayerData(構造体)

作成:ブループリント > 構造体
名前:Struct_PlayerData

変数を追加します。
・Transform (Transform)
・Level (String)

このTransform (Transform)とLevel (String)のデフォルト値がゲームのスタート位置になります。
テストプレイであってもTransform (Transform)は適用されますので、Locationの入力は必須です。
セーブ&ロードシステムを実装してTransform (Transform)のデフォルト値を決めたら、テストプレイ時の「現在のカメラ位置」「デフォルトのプレイヤースタート」は無効になります。

BP_SaveGame(SaveGame)

作成:ブループリント クラス
親クラス:SaveGame
名前:BP_SaveGame
変数を追加します。
名前はPlayerDataで型はStruct Player Data。

BP_GameInstance

作成:ブループリント クラス
親クラス:GameInstance
名前:BP_GameInstance
ノードを組む

イベント Initを配置して線を伸ばしてDoes Save Game Existを作成。これは既にセーブデータが存在するか確認するものです。そのSlot Nameから線を伸ばして変数へ昇格。名前はSlot Nameのままで、コンパイルしてからデフォルト値をSaveGame0にします。SaveGame0はニューゲーム扱いです。
Return Valueから線を伸ばしてブランチを配置。
ブランチのFalseから線を伸ばしてCreate Save Game Objectを配置。Save Game ClassはBP Save Gameにします。
Return Valueから線を伸ばして変数へ昇格。名前をSaveGameRefにします。
関数を追加します。名前はSavePlayerData。
ノードを組む

変数SaveGameRefをGetして線を伸ばして、Set Player Dataを配置。Player Dataから線を伸ばしてSave Player Dataに繋げる。実行ピンも繋ぐ。
セットから線を伸ばしてSave Game to Slotを配置。
Save Game ObjectにSave Game Refを繋ぐ。
Slot Nameには変数Slot NameをGetして繋ぐ。
セーブはセーブメニューから行います。セーブボタンをクリックした時にSlot Nameが割り当てられますから、デフォルト値のSaveGame0にセーブされることはありません。
関数を追加します。名前はLoadPlayerData。
ノードを組む
Load Player Dataから線を伸ばしてLoad Game from Slotを配置。Slot Nameには変数Slot NameをGetして繋ぐ。
Load Game from Slotから線を伸ばしてCast To BP_SaveGameを配置。
変数SaveGameRefをSetして、Cast To BP_SaveGameから2本の線で繋ぐ。
セットの右下から線を伸ばしてPlayerDataを取得
関数LoadPlayerDataを選択し、詳細のアウトプットを+して追加。
出現したリターンノードPlayerDataから線を伸ばして入れる。アウトプットのBooleanの方は削除してください。
セットリターンノードも繋げる。
編集 > プロジェクト設定 > マップ&モードのゲームインスタンスクラスをBP_GameInstanceにしておきます。

BP_Player(プレイヤーブループリント)

関数を追加します。名前はLoad Game。
ノードを組む

Load Gameから線を伸ばしてCast To BP_GameInstanceを配置。
ObjectにはGet Game Instanceを配置して繋ぐ。
As BP GameInstanceから線を伸ばして変数へ昇格。名前はBP_GameInstanceにします。
セットからLoad Player Dataを配置。ターゲットもセットと繋ぐ。
Player Dataから線を伸ばして変数へ昇格。名前はPlayerData。
セットの右下のピンで右クリックして構造体ピンを分割
セットから線を伸ばしてSet Actor Transformを配置。分割した構造体ピンからNew Transformへ繋ぐ。
関数Load Gameはゲーム開始時に読み込みたいものですから、イベント BeginPlayから続くノードの末尾に配置してください。
Load GameのButtonをクリックした時点では、セーブデータの中のLevelのみを使って遷移しています。それ以外の項目をこうやってプレイヤーブループリントで割り当てます。

W_Load(ウィジェットブループリント)

作成:ウィジェットブループリント
親クラス:User Widget
名前:W_Load

Canvas Panelを配置。

Canvas Panelの子としてVertical Boxを配置。
・アンカー 中央
・位置 X:0 Y:0
・サイズ X:1280 Y:640
・Alignment X:0.35 Y:0.6

Vertical Boxの子としてButtonを配置。
名前をLoad1にします。

Load1(Button)の子としてTextを配置。
・Horizontal Alignmentを左から2番目。
・Vertical Alignmentを左から2番目。

Load1(Button)を複製して4行にします。
各Buttonを選択して、
・Sizeをフィル。
・Horizontal Alignmentを右。
・Vertical Alignmentを右。

Load Buttonを上からLoad1~Load4に名前を変更する。
各Textの名前をLoad1_TextからLoad4_Textに名前を変更。Is Variableにチェックを入れる。
コンテンツ > TextをNew Gameにしておく。

ロードスロットを開いた時の文字表示
ノードを組む

カスタムイベント LoadGameSavesを追加。
線を伸ばしてSequenceを配置。ロードスロットが4つならピンも4つにする。
Then 0から線を伸ばしてDoes Save Game Existを配置。SlotNameはSaveGame1にします。
線を伸ばしてブランチを配置。2本の線を繋ぐ。
Lave1_TextをGetして線を伸ばしてSetText (Text)を配置。In TextはLoad Game 1と入力。ブランチのTrueから繋ぐ。
これを複製して、数字とButtonを置き換えてSaveGame4まで作成します。
各スロットのテキストを追加して、表示情報を増やすことも可能です
イベント Constructを配置してLoadGameSavesに繋ぎましょう。
クリックしたスロットをロードする
ノードを組む

Load1からLoad4までイベント On Clickedを配置。
Does Save Game Existを配置して、Slot Nameから線を伸ばして変数へ昇格
それぞれのOn Clickedから線を伸ばして変数SlotNameをSetし、Slot NameをSaveGame1~SaveGame4まで入力。
4つともDoes Save Game Existへ繋ぐ。
Get Game Instanceを配置し、線を伸ばしてCast To BP_GameInstanceを配置。Does Save Game Existから繋ぐ。
As BP_GameInstanceから線を伸ばしてSet Slot Nameを配置。そのSlot Nameに変数Slot NameをGetして繋ぐ。
線を伸ばしてブランチを配置、ConditionにはDoes Save Game ExistのReturn Valueから繋ぐ。
True側はCast To BP_GameInstanceAs BP_GameInstanceから線を伸ばしてLoad Player Dataを配置。ボタンをクリックした時にSlot Nameがセットされて、それに対応したセーブデータがロードされるわけです。
Load Player Dataの構造体ピンを分割して、Open Level (by Name)でLevelに遷移します。
False側はロードするデータがなかった場合、つまりNewGameとなります。
マウスHoveredとキーボードFocusの同期
ノードを組む

W_Save(ウィジェットブループリント)

作成:ウィジェットブループリント
親クラス:User Widget
名前:W_Save
0から作成する必要はありません。
W_Loadを複製し、名前をW_Saveとする。
ボタンのLoad1であればSave1に名前変更、Load1_TextであればSave1_Textに変更する。
セーブスロットを開いた時の文字表示

ノードを組む

名前変更した時点でほぼ完成しています。
最後のSetText (Text)のIn TextをLoad Game *ではなく、Save Game *にするだけです。
イベント ConstructからLoadGameSavesに繋ぎましょう。LoadGameSavesだとW_Loadと名前が同じですから、SaveGameLoadsに名前変更しても良いです。

クリックしたスロットにセーブ
ノードを組む

まだセーブ項目が2つですからノードが少ないですが、今後は膨大な数のセーブ項目をここに追加することになります。

BP_Player(プレイヤーブループリント)

ノードを組む

この行程は慣れました。
ウィジェットブループリントを作成したらイベント BeginPlayからウィジェットを作成。変数化もしておく。
そしてMainMenuを閉じる側の末尾に検証済ゲットを配置して有効であるならRemove from Parent

W_MainMenu(ウィジェットブループリント)

以前、Button_Saveとして作っていたものを複製してButton_Loadにする。
Buttonを増やすとイベント On HoveredSet StyleへのButtonの追加が必要です。
ノードを組む

Button_SaveとButton_Loadのイベント On Clickedを配置。
Cast To BP_PlayerGet Player Characterを配置し、As BP Playerから線を伸ばしてLoad Game Savesを配置するのですが、W_LoadとW_Saveで名前が同じなのでターゲットを間違えないように。
続けてAdd to Viewportを配置し、TargetにはそれぞれW SaveW Loadを繋ぐ。
この後の処理は、それぞれのウィジェットが重なって表示されないように、消したいウィジェットをGetして検証済ゲットに変換し、Is Valid(有効)の場合にRemove from Parentを繋げています。
今後もメニューを追加する時は、他のメニューが表示されている場合は消すという処理が必要です。

順調で満足

4つのスロットでSave&Loadできるシステムが完成して満足です。
やはりSave&Loadシステムは最初にやっておくべきだと思いました。後付けとなるとセーブする項目がわけわからなくなってしまいます。

コメント

  1. Davis Reburn より:

    great article

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