【Unity】【C#】クエストシステムを実装する 第2回
概要
第2回=================================
5.List<Quest>にデータを持たせる
6.ScrollView GameObjectを作成する(※今回は割愛)
7.QuestStatusを更新する
8.達成済みQuestがあるか判定する
9.達成済み、未達成、報酬受領済みでソートする
10.まとめ ※有料
5.List<Quest>にデータを持たせる
ゲーム中に変化しないデータはScriptableObjectに格納してそこから取得できますが、QuestはQuestStatusが変化するのでMonoBehaviourを継承したGameManagerクラスに格納してゲームに使用します。
また、GameManagerには2.で作成したEntity_Questインスタンスもメンバーに追加し、初期化処理でList<Quest> Questsにデータを読み込みます。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
//2.クエストデータをUnityにインポートする で作成したもの
[SerializeField]private Entity_Quest EQ;
public Entity_Quest _Entity_Quest {
get {
if (EQ == null) {
EQ = Resources.Load<Entity_Quest> ("ScriptableObjects/Quest");
}
return EQ;
}
}
//4.QuestManager.csを作成する で作成したもの
public QuestManager _QuestManager = new QuestManager ();
void Awake ()
{
DontDestroyOnLoad (gameObject);
Load();
InitQuestManager ()
}
//QuestManagerの初期化処理
void InitQuestManager ()
{
if (_QuestManager.Quests.Count < 1) {
//Listの要素を必要なだけnewする
while (_QuestManager.Quests.Count < _Entity_Quest.param.Count) {
_QuestManager.Quests.Add (new Quest ());
}
//ScriptableObjectからデータを取得する
for (int i = 0; i < _QuestManager.Quests.Count; i++) {
_QuestManager.Quests [i].quest = _Entity_Quest.param [i];
_QuestManager.Quests [i].Name = _QuestManager.Quests [i].quest.Descript;
}
Save ();
}
}
//セーブデータのセーブ処理
void Save ()
{
SaveData.SetClass<QuestManager> ("QuestManager", _QuestManager);
}
//セーブデータのロード処理
void Load ()
{
_QuestManager = SaveData.GetClass<QuestManager> ("QuestManager", new QuestManager ());
}
}
Entity_QuestのScriptableObjectは、Resourcesフォルダ下に置いておきましょう。通常はTitleSceneなど最初に読み込むSceneにGameManager(GameObject)を配置してScriptableObjectをアタッチしておきますが、TitleScene以外でテストプレイする際にはGameManager(GameObject)を生成するようにしているため、その場合はResourcesから読み込むようにします。
セーブデータのSave(),Load()に関しては、解説を割愛します。こちらの記事を参照ください。
ゲームを再生してみて、こんな感じでデータが読み込まれていればOKです。
6.ScrollView GameObjectを作成する(※今回は割愛)
5.までで元となるList<Quest>をゲームで使用できるようになりました。ここからはScrollViewのContentにクエスト数と同じだけ子要素GameObjectを突っ込んで、クエストに応じて表示や挙動を変えてあげればOKです。
自分の場合はクエストが450個超もあるため、全部をGameObjectとして配置すると負荷が重くなると判断して、下記のAssetを購入して導入しました。ちなみに導入はぶっちゃけ少し面倒でした。Grid Layoutにも対応してないのも残念。しかし導入さえしてしまえば高速低負担で動作していい感じです。
Assetを使わない場合は下記などが参考になると思います。
こういうのもあります。
データを流し込んでUIに反映させればとりあえずは表示されます。
7.QuestStatusを更新する
表示するまではできましたが、クエストは未達成のものが達成されたり、報酬を受け取ったら受け取り済みになったりと、状態の更新があります。
4.QuestManager.csを作成する で作成したQuestManager中のQuestStateUpdate() でこの更新処理を行います。
また、クエスト更新の条件として使うためのフラグ管理クラスや、バトル数などをカウントするカウンタークラスを作成する必要があります。
[System.Serializable]
public class GameFlags
{
//プレイヤー名が変更されるとtrueになる
public bool isPlayerNameChanged = false;
//新しくデッキを編成するとtrueになる
public bool isNewDeckBuilt = false;
//ヘルプメニューを一度でも開くとtrueになる
public bool isHelpMenuOpened = false;
//属性相性表を一度でも開くとtrueになる
public bool isAttributionTableOpened = false;
}
[System.Serializable]
public class GameCounter
{
//バトル数(バトル後に加算される)
public int BattleCount = 0;
//勝利数(バトル勝利後に加算される)
public int WinCount = 0;
//敗北数(バトル敗北後に加算される)
public int LoseCount = 0;
}
ここに記載しているのはサンプルなので、実際は自分のゲームに合わせて設定してください。
そしてGameFlagsクラスとGameCounterクラスもGameManagerに持たせます。メンバーに追加して、Save(),Load()の対象にも追加します。
これでようやくQuestStateUpdate ()の中身を書けるようになりました。引数にGameMangerを設定して、GameFlagsやGameCounterを参照できるようにしてます。
public void QuestStateUpdate (GameManager GM)
{
//未達成のクエストを抽出してListに格納
var NotAchievedQuests = (from x in Quests
where x._QuestState == Quest.QuestState.NotAchieved
select x).ToList ();
//for文で順々に処理する
for (int i = 0; i < NotAchievedQuests.Count; i++) {
//QuestIDで条件分岐させる
switch (NotAchievedQuests [i].quest.QuestID) {
case 100: //プレイヤー名を変更する
if (GM._GameFlags.isPlayerNameChanged) {
NotAchievedQuests [i]._QuestState = Quest.QuestState.Achieved;
}
break;
/*中略*/
case 200: //1回バトルする
if (GM._GameCounter.BattleCount > 0) {
NotAchievedQuests [i]._QuestState = Quest.QuestState.Achieved;
}
break;
/*以下略*/
}
}
}
ちなみにcase文は1個ずつ手書きだと大変なので、スプレッドシートの文字列結合(""&"" でできる)を使ったりして共通部分を自動生成すると手間を省けます。今回の例ではQuestIDとif文条件だけそれぞれ異なるので、それらをG列で結合させてます。
//G列
="case "&$C2&": if ("&$F2&") {NotAchievedQuests [i]._QuestState = Quest.QuestState.Achieved;} break;"
8.達成済みQuestがあるか判定する
達成済みクエストがある場合は「New」などを画面に表示して、プレイヤーにクエストを確認するよう促すUIがあると快適さが増します。
前述したQuestStateUpdate()内で行ってもいいですが、ただでさえ行数が多くなる処理のため、外出しにした方がいいでしょう。
//QuestManager内メソッド
//達成済みクエストの有無を返す
public bool isAchievedExist ()
{
//LinqのAny()は1つでもあればtrueを返す
bool isExist = Quests.Any (q => q._QuestState == Quest.QuestState.Achieved);
return isExist;
}
9.達成済み、未達成、報酬受領済みでソートする
達成済みクエストを最上部に表示して、報酬受取済みクエストは最下段に表示いたいので、その順でList<Quest>をソートします。既にQuestStateをその順にしているので、OrderByでソートすれば望む順にソートされます。
//QuestManager内メソッド
//QuestStateでソートしてSortedQuestsにキャッシュする
public void SortQuests ()
{
SortedQuests = Quests.OrderBy (q => q._QuestState).ToList ();
}
10.まとめ
完成イメージ
有料部分では下記について書いてます。支援頂けると今後の記事執筆のモチベーションにもなりますので、よろしければ購入ください🙏
QuestManager.cs まとめ版
GameManger.cs まとめ完成版
テスト用実行Script
クエスト進捗を表示するための備考
ここから先は
¥ 200
この記事が気に入ったらチップで応援してみませんか?