Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Quest Manager Questions

Discussion in 'Scripting' started by Phobiegames, Sep 13, 2019.

  1. Phobiegames

    Phobiegames

    Joined:
    Apr 3, 2017
    Posts:
    113
    Hello! I'm currently working on a quest system for my rpg and have come across a small problem, Im using several different scripts to communicate between each other:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. public class Pickup : MonoBehaviour
    7. {
    8.  
    9.     public string itemName;
    10.  
    11.     public bool isInTrigger;
    12.  
    13.     public GameObject itemText;
    14.  
    15.     private void Update()
    16.     {
    17.         if(isInTrigger == true && Input.GetKeyDown(KeyCode.E))
    18.         {
    19.             PlayerDataManager.instance.questItemNeeded = itemName; // add the string to the player manager list instead of using this single pattern
    20.             PlayerDataManager.instance.questItemList.Add(itemName);
    21.             Destroy(gameObject);
    22.         }
    23.  
    24.         if (isInTrigger)
    25.         {
    26.             itemText.SetActive(true);
    27.         }
    28.         else
    29.         {
    30.             itemText.SetActive(false);
    31.         }
    32.     }
    33.  
    34.     private void OnTriggerEnter(Collider other)
    35.     {
    36.         if (other.CompareTag("Player"))
    37.         {
    38.             isInTrigger = true;
    39.         }
    40.     }
    41.  
    42.     private void OnTriggerExit(Collider other)
    43.     {
    44.         if (other.CompareTag("Player"))
    45.         {
    46.             isInTrigger = false;
    47.         }
    48.     }
    49. }
    a pick up script that adds a item string to a list

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class PlayerDataManager : MonoBehaviour
    6. {
    7.     public PlayerData[] charactersOnTeam;
    8.  
    9.     public string questItemNeeded; // Here we will either use an Array of strings or a list of strings that fills when a item is picked up so the quest trigger can compare properly
    10.     public List<string> questItemList;
    11.  
    12.     public static PlayerDataManager instance = null;
    13.  
    14.  
    15.     private void Awake()
    16.     {
    17.         if (instance == null)
    18.  
    19.             //if not, set instance to this
    20.             instance = this;
    21.  
    22.         //If instance already exists and it's not this:
    23.         else if (instance != this)
    24.  
    25.             //Then destroy this. This enforces our singleton pattern, meaning there can only ever be one instance of a GameManager.
    26.             Destroy(gameObject);
    27.  
    28.         //Sets this to not be destroyed when reloading scene
    29.         DontDestroyOnLoad(gameObject);
    30.     }
    31.  
    32.     private void Start()
    33.     {
    34.         questItemList = new List<string>();
    35.     }
    36.  
    37. }
    A playerdatamanager that keeps track of item strings that the player has picked up

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. public class QuestManager : MonoBehaviour
    7. {
    8.  
    9.     public Text questNameText;
    10.     public Text questDescriptionText;
    11.     public Text questRewardMoneyText;
    12.  
    13.     public Text questFinishedText;
    14.     public Text questFinishedMoneyText;
    15.  
    16.     public GameObject questTextBox;
    17.     public GameObject questCompleteBox;
    18.  
    19.     private QuestData questDataCollect;
    20.     public List<QuestData> questListToCheck;
    21.  
    22.     public static QuestManager instance = null;
    23.  
    24.     private void Awake()
    25.     {
    26.         if (instance == null)
    27.  
    28.             //if not, set instance to this
    29.             instance = this;
    30.  
    31.         //If instance already exists and it's not this:
    32.         else if (instance != this)
    33.  
    34.             //Then destroy this. This enforces our singleton pattern, meaning there can only ever be one instance of a GameManager.
    35.             Destroy(gameObject);
    36.  
    37.         //Sets this to not be destroyed when reloading scene
    38.         DontDestroyOnLoad(gameObject);
    39.     }
    40.  
    41.     private void Start()
    42.     {
    43.         questListToCheck = new List<QuestData>();
    44.         questTextBox.SetActive(false);
    45.         questCompleteBox.SetActive(false);
    46.     }
    47.  
    48.     public void CollectQuest(QuestData questData)
    49.     {
    50.         questNameText.text = "Quest Name: " + questData.questName;
    51.         questDescriptionText.text = "Description: " + questData.questDescription;
    52.         questRewardMoneyText.text = "Gold: " + questData.rewardMoney;
    53.         questDataCollect = questData; //TODO Instead of using a single pattern quest system, here we will add the current quest data to a quest data list here to be stored later.
    54.         //When we need to find the quest data needed for a specific quest, we will iterate using a for loop to find the string name of the quest or a quest id number, then use the proper quest data
    55.  
    56.         questTextBox.SetActive(true);
    57.     }
    58.  
    59.     public void CompleteQuest()
    60.     {
    61.         //Set quest as inactive
    62.         questDataCollect.questActive = false;
    63.  
    64.         //Contact gamemanager and add quest data rewards
    65.         GameManager.instance.currentMoney += questDataCollect.rewardMoney;
    66.  
    67.         //Set quest as completed
    68.         questDataCollect.isComplete = true;
    69.  
    70.         questFinishedMoneyText.text = "You were rewarded with " + questDataCollect.rewardMoney + "G" + " enjoy your new loot!!";
    71.         questFinishedText.text = "You've Completed the " + "'" + questDataCollect.questName + "'" + " quest good job for helping your fellow Npc!";
    72.  
    73.         //check for quest name to comeplete then remove quest from list
    74.         questListToCheck.Remove(questDataCollect);
    75.  
    76.         questCompleteBox.SetActive(true);
    77.     }
    78.  
    79.     public void AnswerYes()
    80.     {
    81.         questDataCollect.questActive = true;
    82.         questListToCheck.Add(questDataCollect);
    83.         questTextBox.SetActive(false);
    84.     }
    85.  
    86.     public void AnswerNo()
    87.     {
    88.         questTextBox.SetActive(false);
    89.     }
    90.  
    91.     public void CloseRewardBox()
    92.     {
    93.         questCompleteBox.SetActive(false);
    94.     }
    95. }
    The quest manager itself that keeps track of all the quests

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. [System.Serializable]
    6. public class QuestData
    7. {
    8.     public string questName;
    9.  
    10.     [TextArea(3, 10)]
    11.     public string questDescription;
    12.  
    13.     public string questItemNameNeeded;
    14.  
    15.     public int rewardMoney;
    16.     public bool questActive = false;
    17.  
    18.     public bool isComplete = false;
    19.     public bool hasRequiredItem = false;
    20. }
    Quest data container

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class QuestTrigger : MonoBehaviour
    6. {
    7.     public QuestData questData;
    8.  
    9.     public bool isTriggerArea;
    10.     public bool isInConversation;
    11.  
    12.     public GameObject npcNameText;
    13.  
    14.     public void TriggerQuest()
    15.     {
    16.         QuestManager.instance.CollectQuest(questData);
    17.     }
    18.  
    19.     private void Update()
    20.     {
    21.  
    22.         if (isTriggerArea)
    23.         {
    24.             npcNameText.SetActive(true);
    25.         }
    26.         else
    27.         {
    28.             npcNameText.SetActive(false);
    29.         }
    30.  
    31.         //if questdata string is the same as the playerdatamanager string then quest complete is true;
    32.         if(PlayerDataManager.instance.questItemNeeded == questData.questItemNameNeeded)
    33.         {
    34.             questData.hasRequiredItem = true;
    35.         }
    36.  
    37.         if (isTriggerArea == true && Input.GetKeyDown(KeyCode.E) && questData.questActive == false && questData.isComplete == false)
    38.         {
    39.             TriggerQuest();
    40.             isInConversation = true;
    41.         }
    42.  
    43.         if(isTriggerArea == true && Input.GetKeyDown(KeyCode.E) && questData.isComplete == false && questData.hasRequiredItem == true && questData.questActive == true)
    44.         {
    45.             QuestManager.instance.CompleteQuest();
    46.         }
    47.     }
    48.  
    49.     private void OnTriggerEnter(Collider other)
    50.     {
    51.         if (other.CompareTag("Player"))
    52.         {
    53.             isTriggerArea = true;
    54.         }
    55.     }
    56.  
    57.     private void OnTriggerExit(Collider other)
    58.     {
    59.         if (other.CompareTag("Player"))
    60.         {
    61.             isTriggerArea = false;
    62.         }
    63.     }
    64. }
    65.  
    then lastly the quest trigger. The problem at hand between these scripts, is it only works in a single pattern rather than dynamically keeping track of multiple quests at once. In the pick up script, the item has a name (string) that is suppose to be added to the playermanager. So that later the questmanager can check to see if we have said item to check if a quest is completed. But specifically this isn't working because of this bit here:
    Code (CSharp):
    1.     public void CollectQuest(QuestData questData)
    2.     {
    3.         questNameText.text = "Quest Name: " + questData.questName;
    4.         questDescriptionText.text = "Description: " + questData.questDescription;
    5.         questRewardMoneyText.text = "Gold: " + questData.rewardMoney;
    6.         questDataCollect = questData; //TODO Instead of using a single pattern quest system, here we will add the current quest data to a quest data list here to be stored later.
    7.         //When we need to find the quest data needed for a specific quest, we will iterate using a for loop to find the string name of the quest or a quest id number, then use the proper quest data
    8.  
    9.         questTextBox.SetActive(true);
    10.     }
    questDataCollect is a variable i have (more for testing reasons) that is causing the single pattern, I wanna be able to compare against a list of questData containers to find the proper quest to complete when the proper item is picked up. So in particular as it stands, i can got talk to 2 npcs, but questDataCollect will only have the quest data stored of the last npc i talked to. What's the best way of going about fixing this?
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,514
    It sounds like you need some kind of collection, but the question is what kind of collection.

    It might be helpful to draw a diagram of what the API might look like between a given quest and a quest manager.

    A partial list of what you might do against this API is:

    - review ongoing quests
    - select active quest (might not be necessary; maybe they can all be active)
    - receive a quest (it gets added to the manager)
    - update a quest (it gets retrieved and updated)
    - finish a quest (a special case of update that changes it AND removes it from available quests, possibly adding it to a completed or failed list)
    - review completed quests (optional)

    Each of those actions might need to be broken down by who might be wanting to do it. To start the game you might have a script sequence give you a default quest to start out. You might also encounter an NPC who gives you a quest. This brings up the need for more query endpoints:

    "Have I given you this quest?"
    "Has someone else given you this quest?"
    "Have you completed a particular quest?"

    And then it gets into other areas that might need information from other parts of the game, such as,

    "Have you done the prerequisites to even known about this quest?"
    "Are you even the class of player who CAN help me with this quest?"

    Obviously the first thing you need is some unique identifier per quest instance. This identifier would be used for all transactions against the quest and quest manager system. Some of the calls above assume you know the quest identifier, and some of them will actually return a new quest identifier.

    You will probably want some other kind of glue mechanism that can mark arbitrary items as "when acquired / delivered / destroyed / combined, I can solve quest X..." and then the item delivery mechanism can look for that mark and say "Hey, this thing ALSO serves to finish this quest right here."

    And finally you can put debugging API calls into this quest system that let you build UI that can dig into it and show you the developer stuff that isn't normally revealed to the player during play.