How create a Quest System in Unity | RPG Style | Including Data Persistence

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey everyone my name is Trevor and in this video we're going to build an RPG style Quest system in unity we'll start with this simple project where we can run around and collect these coins and gems and then we'll expand on that project by adding a quest system where each quest's information is defined in a designer friendly way using scriptable objects we'll be able to define the requirements for starting a quest and the rewards for finishing a quest and for the actual content of the quest will be able to add any number of quest steps which can be as simple or complex as we need them to be we'll also keep track of the state information relating to each quest which can be used in a bunch of different ways for example displaying different icons depending on the state of that Quest or even persisting that state between playthroughs so that the player can pick up exactly where they left off the focus of this video is to set up the foundation for a quest system that you can build off of for your own project and as part of that we'll be implementing two quests one where we need to collect five coins and another where we need to visit these three pillars in a specific order and even though those are both fairly simple examples the system we're going to build will be able to handle pretty much any type of RPG style Quest and while we aren't going to go too far beyond the fundamental system in this video I do have some future videos planned to expand on this system adding things like a quest log and Quest Focus dialogue system so keep an eye out if those things are of interest to you and of course the example project we're going to start with as well as the end result can be found on GitHub which will be linked in the description of this video but before we get into how all of this is going to work I wanted to give a quick disclaimer on a couple of things first I would consider the system we're going to build to be a fairly intermediate level system simply because of all of the tools and techniques that are going to go into it I won't be covering Topics in this video like Unity events and event driven architecture unity's new input system Unity lifecycle methods prefabs and object instantiation scriptable objects serialization and deserialization and c-sharp Basics like data structures and object inheritance all of which are going to be important to understand if you want to get the most out of the system that we're going to create if you're unfamiliar with any of those topics I'm certainly not trying this guy scare you off but proceeding your own caution through this video and just keep in mind that if you hit a point in the video where you're not sure how things are being done it's likely one of those topics you'll want to learn more about and second a quest system is a particularly difficult topic to cover because when it comes down to it the system itself can be vastly different depending on your game and what you're going for or in other words like with most game systems but especially with this one there is no one-size-fits-all approach be sure to watch the how it's going the work section of this video or even skip ahead to the creating one more Quest timestamp at the end of this video to see and understand what we're going to build and if that makes sense or not for your own project and preferences that said I do think the quest system presented in this video can act as a good foundation for many RPG style games and I really hope you're able to use it as a foundation for your own game in some way and one last thing I need to mention is that this video is sponsored by unity chances are if you clicked on this video then you're likely already using unity in some way but what you might not have heard yet is that as of putting out this video Unity has just released their 2022 long-term support version this release is packed full of enhancements to the game engine with some of the bigger features being dots more support for multiplayer games including netcode for entities various improvements to the high definition render Pipeline and the universal render Pipeline and improvements to help optimize your game for numerous platforms I'm personally most excited about the improvements to the universal render pipeline since that's what I typically use when working with unity more specifically if you've ever gone crazy with Lighting in the Universal render pipeline you might have run into issues with the per object light limit count where an object couldn't have more than eight lights affecting it however with the 2022 LTS version they've added forward plus rendering that gets rid of that limitation meaning you can go crazy with Lighting in your game as long as you're still within the per camera limit which is of course much higher anyways if you want to see more of what the 2022 LTS version of unity has to offer you can use the link in the description of this video to go check out a blog post which acts as an excellent overview a huge thank you to Unity for sponsoring this video and with all of that out of the way let's get into how we're going to go about designing a quest system in unity when thinking about what a quest actually is to start out there are usually requirements that the player needs to meet before request is available at the start these can be a lot of things from the players level to having a specific item in their inventory to having completed other prerequisite quests and so on once the player meets those requirements they're able to start the quest in which they're sent off to do something or multiple things to complete the quest some common examples of this in RPG games are Dux things like collect five coins or kill 10 monsters or talk to or visit X like talking to another NPC or simply visiting a location or escort an NPC to X and so on then once we've completed all of the steps for request we can finish it and claim some rewards like gold experience items and more the typical RPG Quest is going to look something like this so that's what we're going to stick with in this video for our definition of what a quest is however this is all static information about a quest or in other words information that doesn't change after we've defined it one of the main challenges with implementing a quest system is keeping track of all of the different state information about each quest for the quest itself we'll want to keep track of its current progression state for example if we aren't meeting the requirements to start it yet if we can start it if it's currently in progress if we can finish it or if we have finished it then on top of that if the quest is in progress we also have to keep track of which Quest step we're on and the variable state within that step which is especially tricky for persisting the quest State because each step might have different data types that we need to keep track of we'll talk more about that specifically towards the end of this video but for now let's come up with a design that'll work for both story and all of this static information as well as keeping track of this state information in unity we can store all of the static information for a particular quest in a scriptable object that we'll call Quest info so where we'll have some unique ID for the quest a display name requirements to start the quest which for this video we're only going to worry about the player's level and prerequisite quests Quest steps which are going to be the content of the quest and we'll get to how that's going to work here in just a bit and rewards for completing the quest which for this video we're only going to worry about player experience and gold we could store this information using mono behaviors instead but storing this information in scriptable objects keeps that information seen independent and is also more designer friendly to modify and maintain so that's why we're using scriptable objects here so next let's talk about the quest steps which are in my opinion the trickiest part to handle in a quest system because they can be anything from a collect X quest to visiting an NPC or solving a riddle or whatever else you can imagine for your game a single Quest will always have one or more Quest steps an assumption we're going to make for this system is that when a quest has multiple steps those steps will always be done consecutively for example let's say we have a quest called create a master gem where the first Quest step is to collect five gems and the second Quest step is to take those five gems to some location in the game the reason we'd make these two separate steps is because the second step is dependent on us completing the first and we'd want to ensure that they're done in that order however let's take another example where we have a quest called find stuff where we have multiple things that we want to do in the quest but we also don't care about the order in which those things are done we might be tempted to Define each of those things as different Quest stops because they're different actions but by our definition that would make it where we need to find each of those things consecutively so instead we'd want to include all of those things in a single Quest step so that they can be done in any order the big takeaway here is that by this system's definition of a quest step the steps for request should be broken into pieces we need to do consecutively not necessarily by the individual actions that make up the quest as for implementing Quest steps we'll handle this by creating an abstract c-sharp class Called Quest step which will abstract out any code that's common to every step in the game and from there we can extend it to implement more specific Quest steps for example the quest step abstract class will have a finished Quest step method to advance the overall Quest forward then for a more specific Quest step where we want to collect five coins we'd extend that Quest step class and then add the code that keeps drag of collecting those five coins calling that Finnish Quest step method after we've collected all five the main reason for separating it out this way is so that we can cut back on the repeated code between more specific Quest steps and this also gives us a clean interface to interact with Quest steps in a generic way from other c-sharp classes then we can put the collect coins Quest step script on a Prefab game object that can be instantiated into the game's scene once we reach that specific part of the quest and then store that Prefab game object in the quest info scriptable object where of course if we have multiple steps we'll store them in the order that they're meant to be completed in this can be handled in a lot of different ways but I personally like this approach because that Prefab game object can be as simple or as complex as we need it to be for example maybe it's just a single game object with that custom Quest step script attached or maybe it also has a bunch of child game objects that either change something in the game World related to that quest stop or help facilitate a more complex Quest step through to completion so that'll take care of how we set up the quests in unity but it won't cover the state information that we need to keep track of your first thought might be to add State information to the scriptable object but I personally don't recommend doing this as scriptable objects are usually best used to hold static non-changing data including State information in them can cause a lot of confusion as to what's happening when it comes to persisting data outside of the game as well as confusion during development because that information is constantly for sustain in the scriptable object what we're going to do instead is create a c-sharp class Called Quest that'll include a reference to the quest info scriptable object State data for the quest's current progress state which will be an enum with the values requirements not met can start and progress can finish and finished and also State data relating to the quest steps like which Quest step we're on and any specific state to do with a given step in addition to storing that state data we'll also include some methods in that class to make quests easy to interact with and manage like instantiating the current Quest step moving on to the next Quest step and so on and that's the main data structure we're going to create to manage each individual Quest but we'll also need something to manage all of the quests in our game as a whole so we'll create another c-sharp class called The Quest manager the quest manager class will be responsible for managing state for all of the quests as well as orchestrating our movement through the quests we're going to stick with event-driven architecture for this entire system so for the quest manager we'll listen to three events dealing with how we can progress a specific Quest start Quest Advanced Quest and finish quest each which will will include a unique ID for the corresponding Quest start Quest is just how it sounds which will let us start a quest by its ID and hence instantiate the first Quest step for that Quest Advanced Quest will move us on to the next Quest step for that Quest or if there aren't any it'll put us in a state where the quest can be finished and finish quest is also just how it sounds it'll finish the quest and then do whatever it needs to to claim the rewards for that Quest and of course to be able to start a quest we'll want to make sure that we're meeting whatever the quest requirements are so the quest manager will also listen for events relating to those requirements and then keep up to date appropriately for things like prerequisite quests we can check for those internally but for things like the player's level we're going to listen to an event for when the player's level changes and just to note if you have a bunch of different requirements you need to keep track of it could be worth creating a separate class to do that however for this video we're going to keep things simple and just do it as part of the quest manager then when anyquest is updated including when we first initialize the quests on Startup we'll send out a quest State change event that includes the entire Quest the data structure with that any other part of our project can listen for those events and keep up to date with the current state of all Quests for this video we'll have a monobehavior script Called Quest point which will act as the start and or finish point for a quest and each Quest point will listen for the quest air change event to keep up to date with the current state of whatever Quest they correspond to we can then use that information for displaying a visual icon as well as for sending out the appropriate event to start or finish a quest when the player interacts with that Quest point and we'll dig into how these Quest points will work in more detail once we go to implement them later in this video as for advancing quests we're actually going to do that using the quest step class as part of the Finnish Quest step method will automatically send out an advanced Quest event so that the quest manager knows to move on to the next stop and just to keep the scene cleaned up we'll also have that Quest that destroy itself after it's been finished since after it's been finished it's just that code that's hanging around in the scene and that's the basics of how this system is going to work but there's actually one more thing you might have noticed that we need to keep track of and that's the state of any individual Quest step towards the end of this video will dig a bit deeper into exactly how we're going to do this but for now just know that we're going to send out an event containing any state information for that quest stop anytime the state of that Quest step changes which the quest manager will listen to and then store that information with the respective Quest object and with all that state information stored in one spot it'll make it relatively easy to persist the state data for all of the quests outside of the game if we wanted to implement a save and load system but again we'll dig into the finer details of persisting data later in this video when we go to implement that I know that was a ton of information so let's run through an example of what it looks like to create a quest and how this system works as a whole let's say that we wanted to create a quest called collect stuff that has two Quest steps the first step is to collect 10 coins and the second step is to collect 10 gems in this system we'd create two custom scripts for those Quest steps collect coins quest stop and collect Jim's Quest step both of which would extend the quest step class and then contain custom code to keep track of one we've collected 10 of those items and then of course we'd make sure to call the inherited finish quest step method after we finish that step then we'd create prefab game objects for those Quest steps they can be instantiated into the scene we then set up a quest info scriptable object called collect stuff Quest filling it out with whatever requirements and rewards we want and of course most importantly the two Quest step Prefab game objects then when the game starts up the quest manager will take all of the quest info scriptable objects in the project and create Quest objects from them which of course are just a combination of the quest static and state information with some convenience methods to easily interact with that information the quest manager will then put those Quest objects into a dictionary mapped by the unique ID for that Quest so that we can easily look up a quest by its ID when the game starts up all of the quests will be in the requirements not met state and they'll all be sent out through the quest State change events so that other components in the game can initialize appropriately then in the quest manager we'll constantly check and promote quests to the canned start state if the requirements for that Quest have been met which of course will get sent out through the quest State change event so that way other parts of our game like these Quest points can update accordingly once the quest is in the can start State the player can interact with the corresponding Quest point which we'll send out start Quest event and that gets received by the quest manager where we'll change the state from canstar to in progress which you might have guessed will send out a quest State change event to inform the rest of our game about that change and at this point the quest manager will also instantiate the first Quest step for that Quest into the scene which if we recall is a quest step to collect 10 coins with that Quest step in the scene the player can go and collect 10 coins and each time they collect the coin the quest step will send out an event updating that specific State for the step which gets stored by the quest manager with the corresponding Quest object and since updating the quest step state is technically also updating the state of the quest itself each time that happens the quest manager is going to send out a quest State change event for that Quest as well and finally once the player collects all 10 coins the quest step has its finished Quest step method called on it which sends out the advanced Quest event before destroying itself the quest manager will receive this and then move on to the next step in that quest which is collecting 10 gems in which it'll instantiate that Quest that prefab just like it did with the coin step and then the process repeats updating the quest Upstate until all 10 gyms are collected at which Point it'll send out the advanced Quest event and destroy itself except this time when the quest manager receives the event it'll notice that there are no more Quest steps in that Quest meaning that the quest can be finished and therefore changes it over to the canned finish state which as you probably guessed gets sent out as a quest day change event now when we visit that Quest point we can turn in the quest which will send out the finished Quest event where the quest manager will then change the quest state to finished send out the corresponding Quest State change event and do whatever else it needs to to Grant the player the appropriate rewards for this video we'll already have a couple of events set up to increase the player's experience and gold so we'll just need to send out those and that's how it's all going to work next let's take a look at the project that we're going to implement this Quest system into I'm only going to cover the most relevant parts of this project but if you want to dig deeper into how anything here works you can find this exact starting point on GitHub the project we're starting with is a really simple top-down game using unity's Universal render pipeline where the player can run around in this small scene that was built using Unity tile Maps pretty much all of the art in this project comes from a free package on the Unity asset store called tiny RPG Forest where I've just changed up the colors a bit for what I'm using with the exception of these Quest marker icons which I made myself in the game the player can collect coins which increases their gold and collect these gems which gives them experience on the code side both gems and coins are functioning in an event-driven way where when we collect the coin for example we're sending out an event for gold gained in which we have a manager script that listens for those events and keeps track of our total coin count and then sends out another event every time the gold amount changes so that other scripts like our UI that displays the coin count can keep up to date which should hopefully sound a bit familiar if you watch the system design portion of this video since this is essentially a less complex version of how the quest manager we're going to build will behave the events themselves are all self-contained to this game events manager class which is a Singleton that's attached to a game object in the scene which can be referenced from any other script to send or listen to events I personally like this approach because it keeps all of the game's events in a single spot of the project where they're organized into these separate objects by the type of events that they are and for the quest system will be creating a new Quest events object that'll hook into the game events manager to keep track of all of the events relating the quests that said you can see here that all of the events are being set up in the game events manager's awake method which means there could be a race condition if we were to try to subscribe to one of those events in another classes on enable method since those events wouldn't be initialized yet causing the code to error out of course we could subscribe to events in the start method of every class instead but as part of our design we want the quest manager to broadcast the initial state of all of the quests when the game starts up in which case we want all other classes to be subscribed to those events before then in the unity lifecycle so in order to be able to reference the game events manager events in the on enable part of the unity lifecycle we need to ensure that the game events manager script runs before all other scripts which we can do by adding it to the script execution order of the project and placing it before this Default Time block and just to note you typically only want to modify the script execution order like this if you have a really good reason to do so which in my opinion for this case we do but just realize that by doing this we're creating a big dependency in our project on this game events manager to run in this specific order this project is also using unity's new input system which can be installed through the package manager and I already have all of the actions that we're going to need for this video set up those being movement which will be the arrow or wasd keys and submit which will be the spacebar or enter Keys there's also an action for toggling the quest log but we won't be doing anything with that in this video from there I'm using a custom class called input manager which acts like a proxy to Route those input actions through the game events manager giving other scripts easy access to subscribe to those events you can handle input however you want as it's not a very crucial part to the quest system that we'll be building but I wanted to mention this so that at least later in the video when you see me subscribing to events like on submit press you'll have some context for how that's set up and of course there are certainly other things going on in this project and if you want to play around with it yourself this exact starting point can be found on GitHub which is linked in the video's description so finally let's start implementing the design that we came up with first we're going to create the basic structure for dealing with our Quest static data which is that Quest info scriptable object in the scripts folder just to keep organized will create a new folder called quest system and then in there create a new c-sharp script Called Quest info so and then double click it to open it up we can remove these placeholder methods and we'll also change this to extend from the scriptable object class instead of mono behavior and then we'll add the create asset menu annotation to the top of the class where the file name will be Quest info so the menu name will be scriptable objects Quest infoso and we'll set the order as one and in there the first thing we're going to create is a public string ID with a public getter and private Setter and we'll also add this field serialize field portion to the beginning so that it shows up in the unity inspector just so we can see it this ID is pretty important since it's what we're going to use to reference any specific Quest across the entire system and as such it needs to be unique to each Quest there are a lot of ways that we could do this but one way that I learned more recently would be to use the name of the scriptable object itself for this ID since you probably won't have two Quest info SOS with the exact same name we can do that by adding this on validate method and and then adding this if Unity editor and in diff part and in there we'll set the ID to this.name and we'll also set the asset to be dirty in the unity editor but of course if you're expecting your scriptable objects for different quests to have the same name you'll want to set this ID up differently but I think for most projects this works pretty well then we'll add any other static information we need for a quest this includes the display name which will be however we want the name to appear in the game itself any requirements to start that quest which like I mentioned before we're only going to include the player's level and any prerequisite quests as requirements which will just reference the corresponding Quest info scriptable object for any prerequisite quest to determine those the quest step prefabs which will be an array of game objects and any rewards for completing that quest which we're just going to make gold and player experience and that does it for the quest info scriptable object script back in unity will create a new folder directly under assets called resources and then in there we'll create another new folder called quests and just to keep things extra organized we're going to create a new folder in there called collect coins quest which is going to be the first Quest that we're going to create for this project then in there we'll right click go to create scriptable objects and select Quest info so and we'll rename this to collect coins Quest and then shortly after that or sometimes putting something in this ID field can force it to update the ID will change to be the same name as the scriptable object asset just like we wanted and with how we have it set up you actually can't change this field without changing the file name which is a good thing and so if we ever rename the file the ID would change along with it as well so next we'll fill out the rest of this for the collect coins Quest giving it a display name we'll set the level requirement as one and no prerequisite quests and the rewards to something like 50 gold and 250 experience and just to note the way that we're going to get references to these scriptable objects later in this video is going to be by using the resources.loadallline which requires them to be in the resources slash quests folder of the project so the naming here for these folders is actually very important next we'll need to create a quest that prefab to go into this slot so that our Quest actually has some content so let's set that up first we need to create a quest step script that will contain any common code for every single Quest step in our game under these scripts Quest system folder of the project will create a new script Called Quest step and then double click it to open it up we can remove these placeholder methods and we'll add the word abstract up here to make this an abstract class all that means is that this class is meant to be inherited by another class and not meant to be used directly then we'll create a private Boolean called is finished and then start that out as false next we'll create a protected void finish quest step method where in there we'll check if the quest step has already been finished and if not we'll set is finished to true to indicate that it's now finished and then we'll also destroy the game object so that the step cleans itself up from the scene and in between here we'll add a to do comment because eventually we'll want this quest stop to send out an event to advance the corresponding Quest forward we'll expand on this Quest step class more later on but this is a good starting point for now next let's create the more specific Quest step that will extend from the quest step class you can put these kinds of scripts wherever you want but I'm actually going to put it right next to the scriptable object for the quests that we're making this Quest up for rather than in the scripts directory of the project simply because I prefer these Quest specific items to be together so in here we'll create a new c-sharp script called collect coins Quest step and then double click it to open it up we can remove these placeholder methods and instead of extending from mono Behavior will extend from our Quest step class that we just created the idea here is that we'll write code containing whatever custom logic we need for this Quest step and then we'll call that finish quest step method when the quest step is finished and we're ready to move on we're going to make the goal of this Quest step collecting five coins so we'll create two private ends called coins collected which will start at zero and coins to complete which will be five then if we take a look at the coin class for this project it's already sending out an event called coin collected every time we collect the coin which can be found in the game events manager for the project under the misk events object and so with that already set up all we have to do in this script is subscribe to that event in the on enable method and of course unsubscribe from it in the on disable method then we'll create the coin collected method that we're subscribing to down here and in there we'll just check if coins collected is less than coins to complete and if so we'll increment the three coins collected variable then right below that we'll do another check to see if coins collected is greater than or equal to coins to complete in which case we finish this Quest step and therefore can call the Finish Quest step method and with that in place let's set up the corresponding Prefab game object that'll be part of the quest info scriptable object in that same folder where the quest steps script and scriptable object are we'll right-click and create a new prefab then we'll just drag that collect coins Quest step script onto it and just to see this working We'll add it to the scene in enter play mode since coins collected is a private variable in that script we can't see it in the inspector when clicked on the game object but actually if we click on these three dots up here and then we click on debug we'll now be able to see all of the private variables for debugging purposes and if we walk around the project then collect coins we'll see this value increment and when we've collected five coins the object cleans itself up from the scene by destroying itself just like we wanted and After exiting play mode we can remove that Quest that prefab from the scene and then for our scriptable object We'll add that prefab to the list of quest steps by dragging it in so that does it for all of the static data including the quest steps but we also need to manage the state data when working with these quests like mentioned earlier we're going to create a c-sharp class Called Quest to wrap the static information with all of the state information along with some convenience methods to easily interact with all of that information so back in unity we'll create two new scripts under these scripts slash Quest system folder called quest and another one Called Quest State and then open both of them up under the quest State script we'll remove these placeholder methods as well as monobehavior and will also change this from being a class to being an enum for the system our Quest is always going to fall under one of the following states requirements not met for when the player doesn't yet meet the requirements to start the quest can start for when the player does meet the requirements but they haven't yet started the quest in progress for when the player has started the quest can finish for when the player has completed all of the quest steps for that Quest and finished for when the player has turned in the quest and claimed the rewards and then in the quest class we can remove these placeholder methods as well as mono behavior and then we'll create a public Quest info so variable called info which of course represents all of the static data for the cloud test and then we'll also create a public Quest State called State and a private int called current Quest step index then we'll create a public Constructor where we'll pass in the quest info scriptable object for that Quest and set that to the info for this Quest and then we'll initialize the state to be requirements not met and the current Quest step index to be zero so that every Quest starts with that state information then we'll want a method to move on to the next Quest step and so we'll create a new method called move to Next Step which increments the current Quest up index variable thereby moving us on to the next step as you can imagine though incrementing this might put us out of range for the number of quest steps that actually exist in which case we can use that information to tell ourselves that the quest no longer has any content for us to complete so to help with this we'll create another public method that returns a Boolean called current step exists and in there we'll return true only if the current Quest step index is less than the amount of quests that Prefab game objects we have for that Quest and last it would be nice to have a convenient way to instantiate whatever the current Quest step is into the scene and so we'll create another public method called instantiate current Quest step which will take in a transform to act as the parent for the instantiated quest step the first thing we need to do here is get the current Quest up prefab so to keep things clean we'll create a new private method called get currentquest.prefab which returns a game object then in there we'll set the quest step prefab to null and then we'll check if the current step exists by calling that method that we just made and if it does we'll get the prefab out of the scriptable object otherwise that means the index is out of range and will give ourselves some nice warning logging to let ourselves know what happened and then we'll return that prefab at the bottom then in the instantiate current Quest step method we'll call that to get the current Quest step prefab and as long as it's not null we'll instantiate it using object.instantiate passing in the prefab as well as the parent transform that we want to instantiate it under and that does it for the quest class for right now but we'll be coming back to this later to expand on it next we'll start pulling all of these things together with the quest manager class under the scripts Quest system folder of the project will create a new c-sharp script Called Quest manager and then double click it to open it up taking this one step at a time let's first focus on loading all of the quests into a map where we can easily reference a quest by its ID we can remove these placeholder methods and then we'll create a private dictionary that Maps a string to a quest Called Quest map then we'll create a private method that returns a string to Quest dictionary called create Quest map and in there we'll load all of the quest info scriptable objects into an array using resources.load all where the type is Quest info so and the directory within our Resources directory that we want to load from is called quests and just in case you missed it previously that's why the naming here is important because resources.load all Targets the resources folder of the project and within that we're targeting the quests folder specifically from there we'll create a new string to Quest dictionary called ID to Quest map and then Loop through each Quest info scriptable object in a4h Loop and it's a good idea here to check for and log a warning if we end up with any duplicate IDs so we'll do that as well and then we can add that to the map mapping the quest info ID to a new Quest where we're passing in the quest info scriptable object and it looks like I have a typo here so I'll correct that real quick and last at the bottom we'll return the completed map then in the request manager's awake method we'll call that create Quest map method to initialize the map and next we're going to add another private method called gitquest by ID which takes in a string ID and returns a quest in there we'll get the value from the dictionary with this line here but if its null will throw an error and then we'll return the quest the whole reason for having this method is to catch errors if we ever try to access a quest ID that doesn't exist and so whenever we need to get a quest by its ID we'll go through this method instead of accessing the map directly and just so we can see this working we'll make sure the collect coins Quest Got Loaded into the map by using the git method that we just created passing in the unique ID for the collect coins Quest and then we'll log some of the information for that Quest like the display name level requirement and so on just so that we can see it loaded correctly and then back in unity we'll need to attach the quest manager somewhere in our scene so I'm going to do that by creating a new game object called quest manager under the managers game object and then drag on the quest manager script and if we enter play mode we'll see some logging in the console on Startup with information that matches the collect coins Quest and if you've made it this far awesome job that means our Quest info scriptable object is getting loaded in and the quest data structure is being created appropriately which is a huge part of this system next let's actually set up the quest manager to be able to start advance and finish our quests and the first step in doing that is going to be adding the quest events that we talked about during the design section of this video which if you remember will have three main events to start advance and finish any specific Quest as well as an event that gets sent out from the quest manager that will notify other scripts that a quest state has changed of course there's also this event for when the player's level changes but that's already been set up in the player events class and gets sent out by the player level manager script whenever the player's level changes and there's also this Quest step State change event but we're not going to worry about that one until a bit later in the video back in unity we'll create a new c-sharp script under the script events folder which is where all of the scripts that hold events for this project live Called Quest events and then double click it to open it up we can remove these placeholder methods along with mono behavior and we'll also add using system to the top then we'll Define an event like this where we create a public event action and then in these brackets will be the type type of data we're going to pass along which will be a string for the quest ID and then the name of the action which is going to be on start Quest then we'll add a way to invoke that action by creating a public void method called start quest which will take in that string ID and as long as onstart Quest is not null we'll call that with the ID and if this is confusing how we have this set up I highly recommend the resource I put in the description of this video which follows this pattern and explains it in a bit more detail so that's it for the start Quest event and these are all going to look really similar so we'll copy and paste this a couple of times and change things to also create the advanced Quest and finish quest events and last we'll do the same for the quest State change event but instead of a string we're going to pass along the entire Quest object and just like that we've set up the events that we want to use but we still need to hook these up to the game events manager script so that they can be referenced across the project so in the game events manager script we'll create a new Quest events variable Called Quest events and initialize it to a new Quest events object in the awake method and with that now we'll be able to reference and use those events next back in the quest manager class so we can get rid of these debug.log statements that we added in the awake method and then let's create three methods for starting advancing and finishing a specific Quest we'll fill these out later but for now we'll just put a to-do comment and some debug logging in each one and then we'll also create on enable and on disable methods subscribing to each event in on enable and of course unsubscribing in on disable and as part of the overall design another thing that we want to do is broadcast the state of every Quest on Startup so that way other scripts know how the initial state looks for every Quest we can do this by creating a start method and in there Loop through each of the quest values in the quest map and then send out an event by calling game eventsmanager.instance.questevents not Quest State change and passing in the quest so next we'll need a way to actually trigger these events from the game with how this Quest system is designed we can trigger the start Advance or finish events from anywhere but like I mentioned in the system design portion of the video we're going to trigger the start and finish events from a quest Point that'll exist in our game and the Advanced Event from the quest step itself so let's create the quest Point next so that way we have the ability to start and finish a quest and then we'll come back to actually fill these methods in with some Logic the idea with the quest point is going to be really simple it'll be a game object with a circle collider so that way we can tell if the player is in range or not and it'll also reference a quest info scriptable object for the quest that corresponds to that point if the player is in range and presses a button and the current state of that Quest is in the can start State we'll send out a start Quest event or if the quest is in the canned finish State we'll send out the finish quest event and then to top things off we'll also add a couple of configuration booleans for whether or not that Quest point is the start Point finish point or both back in unity under the quest system folder will create a new c-sharp script Called Quest point and double-click it to open it up we can remove these placeholder methods and we'll also add a require component annotation to the top to require the circle collider 2D component to be attached to the same game object that this script is attached to then we'll add a Boolean for if the player is near and an ontrigger enter 2D and on trigger exit 2D methods we'll check if the incoming collider is the player which in this project we can do by checking for the player your tag since the player is tagged with the player tag in the scene and then we'll set the players near Boolean appropriately so that'll let us know if the player is near but as mentioned we also need to keep track of the specific Quests for this point so at the top We'll add a private Quest info so variable Called Quest info for Point as well as a private string for the quest ID and a private Quest State for the current Quest State then in the awake method we'll set the quest ID to be equal to the quest info scriptable objects ID and will also subscribe to the onquest state change event in on enable and unsubscribe and on disable to a method Called Quest State change then we'll create a method Called Quest State change and all we need to do here is check if the quest's ID matches the ID for this Quest point and if it does we'll update the current Quest state for this Quest point and we'll even put a debug.log statement in here for now just so we can see what's happening a bit more clearly and next to actually interact with this Quest point will subscribe to the on submit press event and on enable and unsubscribe in on disable which as I mentioned in the project overview part of this video is how we'll detect if the submit button has been pressed but of course use whatever input logic that you prefer and with that we'll create the submit press method which will get called whenever the submit button is pressed and in there will return right away if the player isn't near so that nothing happens but if the player is near for right now we'll just send out all three of those Quest management events just so we can make sure everything is working as expected so far back in unity let's set this up in the scene we'll create a new game object and call it Quest point and then attach the quest Point script to it which should automatically attach a circle collider 2D but if it doesn't you can add that manually and on the circle collider 2D component we'll make sure that the is trigger checkbox is checked and will also add a child game object called visual add a Sprite renderer component to that and then drag in some kind of sprite so that way our Quest Point has a visual reference for where it's at in the scene then we'll also make the circle collider a bit bigger for this video we'll just give it a radius of two and with all of that set up we'll drag this into the prefabs folder of the project to make it a prefab and of course zero out the transform if necessary then we can drag in the collect coins Quest scriptable object so that way this Quest Point corresponds to that West and we'll move this Quest point right over here in our scene then we can enter play mode to check this out in the console logs we'll see that when the game starts up the quest point was updated with the current Quest state of that Quest just like we wanted and if we're near the quest point and press the submit button which is the space bar or enter button in this project we'll see those methods in the quest manager getting called showing that the communication using events is working just like we want it to back in the quest Point script I'm going to get rid of this debug.log statement and we'll also delete sending out these three events here what we actually want to do is start or finish the quest based on both a configuration value for what this Quest Point represents as well as the current state of the quest so at the top we'll create a couple of Boolean variables called start point and finish point then when the submit button is pressed We'll add a conditional statement saying that if the quest's current state is can start and this is a start point then send out the event to start the quest otherwise if the current state is canned finish and this is a finish point we'll send out the event to finish the quest and then back in unity just for fun we'll duplicate the quest point we have with control plus d on Windows and then make the first first one the start point and the second one the Finish point and now if we enter play mode we'll see that pressing the submit button near one of these actually doesn't do anything right now and that's of course because our Quest is still in the requirements not met State and it needs to be in the canned start state if we want to be able to start it so before we start adding a bunch of logic to the quest manager to make those transitions I think it would be really nice to have a visual indicator that helps communicate the quest State I debated what point in the video we should add this but I think now is a really good time because it's going to be really helpful in understanding the flow through this system as we take this example Quest from not having requirements all the way to finishing it and everything in between but if you really don't care about having a visual indicator and just want to get on with implementing the brains for this Quest system you can skip to the next time stamp and you shouldn't miss anything too important so for the visual indicator we're going to have four icons in total this great exclamation point if we're not meeting the requirements yet to start that Quest this yellow exclamation point if we can start the quest this gray question mark when the quest is in progress and this yellow question mark when we can finish the quest in these scripts Quest system folder of the project will create a new c-sharp script Called Quest icon and double-click it to open it up we can remove these placeholder methods and then we'll add serialize field private game object variables for all of those icons next we'll create a public void set State method that takes in the new Quest State as well as variables for start point and finish Point since we might have an icon that only acts as the start or finish for a quest and in there we'll set all of the icons to be inactive up at the top and then create a switch statement for the new state including all of the quest State options if requirements currently aren't met and this is a start point we'll set the requirements not met icon to be active likewise if we can start the quest and this is a start point we'll set the start icon to be active and of course we'll do the same for the other states but instead checking if it's a finish point and using the corresponding icon for that state and if the quest state is finished we don't want to show an icon at all so we'll leave that as is and now back in the quest Point script we're going to add a private Quest icon variable Called Quest icon and we're going to assume that the quest icon is going to be a child game object to the quest point so we can use git component in children to get a reference to it and finally anytime we update the quest State we'll call that set State method on the quest icon passing in the appropriate variables back in unity let's set this up by creating a new game object called quest icon and then we'll create a child game object called exclamation gray attach a Sprite renderer component drag in this exclamation point icon from the art folder of the project and then change the color to be gray then we can duplicate this with Ctrl D on Windows renaming the next one to exclamation and changing the color to yellow and finally we can duplicate both of those renaming the other two to question gray and question and then we can drag in the question mark icon and on the parent game object we'll attach that Quest icon script that we created and then drag in the icons to the appropriate slots and I'm going to make this its own prefab by dragging it into the prefabs folder of the project and zeroing out its positional values and then we'll drag this onto one of the quest Point objects position it accordingly and apply it to the quest Point prefab as well so that it shows up on both and now if we enter play mode we should see a gray exclamation point icon but only on the start point indicating that we have a quest that can be started here but we aren't yet meeting the requirements to start it and now that that visual indicator is set up it'll be a lot more clear to us as we move through the different states for a quest so next let's start adding the logic required the transition from the requirements.net state to the can start State back in the quest manager script we'll create a new private method called changequest state which takes in a string ID and the quest state that we want to change to then in there we'll get the quest using the gitquest by ID method that we made earlier and then change the quest.state variable to the new state and finally we'll send out an event indicating that the quest state has been changed so now anytime we want to update the state for request we can call this method and it'll always send out that event after updating the state to keep the other parts of our system informed now as for going from the requirements not Med state to the can start State we'll handle this entirely within the quest manager by constantly checking if requirements have been met for any of the quests that are in the requirements not met state but of course to be able to do that since the player's level is one of the requirements we need to keep track of that so at the top of this script We'll add a private end for the the player's current level and as mentioned earlier there's already an event setup that gets sent out every time the player's level changes so we'll subscribe to that in on enable and of course unsubscribe a non-disable and then we'll create a method called player level change where we'll set the current player level to the most up-to-date level and so that'll keep the player level requirement up to date so next let's create a private method called check requirements met that takes in a quest and returns a Boolean for whether or not that Quest meets the requirements and in there we'll create a Boolean called meets requirements and set it the true with the goal of proving it to be false throughout this method's logic first we'll check the level requirements and if the player's level is less than the requirement we'll set meets requirements to false then we'll check for any other Quest prerequisites by looping through all of them that are defined in the quest info scriptable object setting meets requirements to false if any of those aren't already finished and then we'll return that meets requirements Boolean at the end and with this method we can check if any Quest is meeting the requirements to be able to start it last to bring this all together we'll add an update method in which we'll Loop through all of the quests in the quest map and if a quest is in a requirements not Med State and they pass the check for this check requirements met method then we'll promote that quest to the canned start State and now with our Quest requirements for the coin collected Quest being level 1 and no Quest prerequisites will hop back into play mode and back in play mode we'll see that the visual indicator is a yellow exclamation point indicating that we can start the quest and if we press the submit button near that start point we'll see some console logging indicating that the quest manager start Quest method was called just like we want it to be and if we were to exit play mode change that level requirement to level 2 and then enter play mode again we'll see that we can't start the quest as a level one but if we go around and collect some gems to level up to level 2 we can start the quest after that but for now we're going to keep the level requirement at level 1 for this Quest and so we'll change that back and next we'll actually want to start the quest in this system that not only means changing over to the in progress state but it also means instantiating the first Quest step back in the quest manager script in the start Quest method we'll first get the quest by its ID then call instantiate current Quest step on that Quest object passing in this dot transform so that the Quest step is instantiated underneath the quest manager game object in the scene and then of course we'll change the quest state to in progress using the change Quest State method and now back in unity we can enter play mode again and when we start the quest we'll see that in the scene hierarchy our Quest step gets instantiated and the quest icon also changes to the gray question mark for the Finish point of this Quest indicating that the quest is now in progress and then just to see this working so far we can view the instantiated quest step game object using debug mode in the unity inspector and we should see that it starts with 0 out of 5 coins collected from there we can collect five coins in the quest step destroys itself but we still need to advance the quest forward when that Quest step is finished and so back in the quest stop script you might remember that we added this to do comment for exactly that so let's Implement that next first we'll need to make the quest step knowledgeable about which Quest it's part of so we'll add a private string quest ID to the top then we'll add a public void initialize Quest step method it takes in a string for the quest ID and in there set the quest ID for this step through the quest ID coming in and just to note we're going to have some other things we'll want to initialize the quest step with later in this video so that's why we're doing this in an initialization method rather than just making the quest ID public and finally we can remove that to do comment and send out the advanced Quest event with that quest ID once we finish the step and next in the quest class we'll want to call that initialize Quest that method when we instantiate the current step which we can do by getting the quest up component and then calling initialize Questa passing in the quest ID and if you're looking to optimize this a bit more since instantiation and getting components during runtime isn't the most performant thing you could instead do something like object pooling for the quest steps but in my opinion for most games unless you request step game objects are massive and you're actually seeing a hidden performance when they instantiate I wouldn't worry too much about this and finally back in the quest manager class let's implement the advanced Quest method first we'll get the quest by its ID then we can call the quest dot move to nextstep method which is going to increment the step index and if that new Corinth Quest step exists then we'll instantiate it but otherwise that means there aren't any more Quest steps meaning that we finished the entire quest in which case will change the state to canned finish and back in play mode if we get back to the point where we're about to collect all five of the coins we'll see that when we collect the fifth coin request now moves to the canned finish State as indicated by the yellow question mark icon and if we try to turn this in we'll see that the finished Quest method in the quest manager is being called but we still need to implement that to actually finish the quest and so back in the quest manager we'll fill out the finished Quest method by getting the quest by its ID pulling a method that we'll create in just a moment called claim rewards and passing in the quest and then changing the quest state to finished then we'll make that claim rewards method which is going to be a private void method that takes in a quest and for this project the rewards are always going to be gold and experience which I already have events set up for in this project called gold gained and experience gained so we'll just send out events for those for the gold reward and experience reward amounts and now after all of that if we go back into play mode and get up to the point where we can finish a quest we'll see that we gain the gold and experience rewards for that Quest and the icon also goes away indicating that the quest is finished and just to make sure this whole thing is working with multiple steps we can actually drag that coin collected step prefab into the quest info scriptable object as a second step and then back in play mode we'll see that when we finish that step another coin collected Quest that pops up that we need to complete before turning the quest in of course you're probably not going to have the same Quest step twice in a row but this simple test just ensures that things are set up properly for cycling through multiple Quest steps and after testing that we can remove that second Quest step to put it back how it was with all of that the core of this Quest system is pretty much taken care of but something that's likely pretty important for your game that isn't the most straightforward thing to implement is how to properly persist all of the state information between playthroughs for example if we start the coin collected Quest collect two coins and then save and quit the game we'd expect that when we return to the game The Quest will be in progress with those two coins already collected this is especially difficult in something like a quest system because there's so much state to keep track of for a single Quest there's the overall state of the quest but also which Quest step we're on within that Quest and beyond that every step is going to have its own individual state that differs from one another for example in the collect coins Quest step the state we need to persist is the number of coins we've collected whereas another Quest that might have more complicated state to keep track of such as multiple values that are all different data types and yet another Quest that might not have any state we need to keep track of at all there are so many ways you could go about handling this and it totally depends on your Project's needs what the quote unquote best way is going to be but for this video we're going to handle this by representing every Quest Upstate no matter how complicated as a string and that's because pretty much any state data no matter how complex can be serialized and deserialized to and from a string form whether that's using something like a Json serializer to turn a c-sharp data class into a Json string or just calling tostring on another primitive data type like an integer and we'll also need to add a structure to maintain the state of any individual Quest steps so to do that we'll add a quest up State change event to the project which will get sent out whenever an individual Quest step State changes passing along the quest ID that step is for the index of that Quest step within the Quest and any state data for that Quest step which will be wrapped in a new class we're going to create Called Quest step state which for now is only going to contain that string representing the quest up state but the reason for wrapping this in a class rather than just sending along a string is because there might be things we want to tack onto this in the future for example we could additionally include a status string to be shown in a quest log and so we're planning ahead a bit by putting this in a class right now anyways then in the quest manager class we'll listen to those Quest steps State change events and whenever we get one we'll store that state information with the respective Quest object and so in the quest class we'll need to add an array of quest step States for that class as well as a convenience method to store the quest step State information and then with all of that set up we'll be maintaining the quest step State as part of each Quest object from there we can save that information however we want to persisting data in a game is its own complex topic and I have a whole series on creating a more robust save and load system for a game if that's of interest to you but for this video we're simply going to create a quest data class to act as the model for State data that we want to persist and then serialize that to a Json string and save it using unity's player prefs where the key is going to be the quest ID and the value will be the serialized data and we're simply going to save the game in the on application quit method of the unity lifecycle so that data saves anytime we quit the game and then for loading that information when we're initially creating the quest map we'll check if we have any saved information for each quest ID and if we do we'll create that Quest using that stored data and if we don't we'll just create the quest like we're already doing where the quest is initialized with default values and of course for any quests that are in the in progress State when we load up we'll want to instantiate the current Quest step that they're on initialized with whatever the current Quest step State data is and after that we'll be persisting the entire Quest State including the exact state of any Quest steps so next we're going to do this one step at a time starting with maintaining the quest step State and then we'll move on to actually saving and loading the data in the quest system folder of the project we'll create a new c-sharp class Called Quest step State and then double click it to open it up we can get rid of these placeholder methods as well as mono behave behavior and then we'll create a public string for the state of the quest step then we're going to create two Constructors one that takes in a string for the state and sets that to the state value for the class and another that doesn't take in anything and sets the state to the empty string and will also Mark this class as system.serializable since when we go to save data we'll want to serialize this along with the rest of the quest data and next we'll want to add a Unity event for when a quest step changes so in the quest events class we'll copy and paste this event down here and change the action to be on Quest step State change and the invoking method to be Quest step State change and then we'll have this event transmit a string for the quest ID that the step corresponds to an integer for the index of that step within the quest and finally the quest Upstate class that we just created which as we know contains the state data for that Quest step next in the quest step class we're going to add a baked in way for all of the quest steps to easily send out that event the quest up already knows about the quest ID but for updating the state like we're doing it'll also need to know about the step index so we'll add a private integer for that and then we'll also set it in the initialize Quest step method and then at the bottom we'll add a protected void method called change state that takes in a string for the new state and in there we'll call the quest step State change event we just created passing in the quest ID step index and a new Quest step State object with that new state and now what we'll want to do is call that change State method from any of our Quest steps whenever the state of that step changes so in the collect coins Quest up class will create a new private method just for convenience at the bottom called update State and in there we'll get whatever state string that we want to represent this Quest step which is going to be the coins collected value and so we'll call coins collected.tostring to turn that integer into a string data type and then we can call change State passing in that string next we'll want to call this method anytime our state changes which right now is just when we increment the coins collected variable so we'll call that method there which will broadcast that state change every time the coins collected variable changes next let's add a way to store that Quest up State data in the quest class We'll add a private Quest step State array Called Quest step states to the top of the class and in the quest Constructor we'll that to a new Quest step State array that's the same length as the quest step prefabs length then we'll also Loop through each index and initialize each one to a new Quest step State object so that those values aren't null and then when we instantiate and initialize the current Quest step we now also need to pass along the current Quest step index next we'll add a public method to store the quest up State Theta which will take in a quest step State object as well as the index of that step and as a bit of defensive programming we'll do a check here to make sure the step index is within range of the quest up States array and as long as it is we'll update that state in our array but otherwise we'll log a warning letting ourselves know that something went wrong due to that index being out of range and like mentioned before it'll also be a good idea to have a c-sharp class that acts as our model for the quest data that we want to persist so back in unity we'll create a new c-sharp script Called Quest data and then double-click it to open it up we'll get rid of these placeholder methods and monobehavior and we'll also Mark this class as system.serializable since we're going to serialize it then we'll create variables for everything we want to persist regarding a quest first being the overall Quest a than the current Quest step index and the neqquest step State array and we'll also add a Constructor that takes in and sets those values and now back in the quest class we'll create a public convenience method called gitquest data and in there we'll return a new Quest data object passing in the state current Quest step index and the quest step States array finally in the quest manager class we're going to start pulling all of this together first off we need to listen for the quest step State change events so we'll subscribe to that in on enable and then we'll unsubscribe and on disable then we'll create the quest step State change method that we're subscribing to which is going to take in a string ID into step index and a quest step State object and in there we'll get the quest by its ID and then call that store Quest stepstate method passing along the state and the step index and since this is technically changing the state of that Quest it also makes sense to send out a changequest state event where we'll just keep the state as whatever the current state of that Quest is so that was a lot of little steps and a lot of areas to make mistakes so before we actually go to save this data let's make sure that it's working as expected so far in the quest manager class we'll create an on app equip method and in there we'll Loop through each quest in the quest map values and all we're going to do for right now is get the quest data for that Quest and then add some debug.log statements to show a bunch of that state information like the quest State the current Quest step index that we're on and all of the quest steps tape being stored and then we can enter play mode start that Quest and collect two coins and then when we exit the game we'll see that state information print out in the console which looks to be correct we can see that the quest was in progress we were on the first Quest step which is index 0 and we've collected two coins so far and with all of that the state data we want to persist is neatly all in one place and ready to be saved so next let's do exactly that back in the quest manager class we'll create a new method called save quest that takes an aquest object and in there we're going to do a try catch block since there are a lot of things that can go wrong with serializing and saving data so in the catch blog we'll log an error saying that we failed to save the quest and in the try block we'll get the quest data from that Quest and then use the Json utility.2 Json method to turn that Quest data into a serialized Json string and then we'll just store that string in play player perhaps under the quest ID by calling playerpreps.setstring and passing in the ID for that Quest as well as our serialized data and just to note if you're having issues with Json utility a more robust alternative is json.net but of course use whatever you'd like to hear or whatever makes sense for your own save and load system and again you probably don't want to use player prefs to store this kind of information long term and I want to make it clear that we're only using player perhaps in this video as a quick to implement example anyways just so we can see what this looks like we'll add a debug.log statement here printing out the serialized data that's being saved and then in the on application quit method we'll remove all of these debug.log statements and instead call the save quest method for each Quest and if we enter play mode make our way halfway through the quest and then exit play mode we'll see that serialized Json string in the console that contains all of that state data and with that looking to be correct I'm going to remove that debug.log statement but if for some reason yours doesn't look correct or it's missing information it's likely that something isn't marked as serializable within the data that you're trying to serialize or it's also possible that whatever serial realizer you're using can't handle a specific type and in the latter case upgrading to something like json.net might fix your issue so that takes care of saving but of course we want to load the data as well so let's do that next we're going to start from the quest step class and then work our way up and so in the quest up class We'll add a protected abstract void method called set Quest step state which will take in a string for the state then when we initialize the quest step we'll also pass in a string for the quest step State and if that string both isn't null and isn't the empty string we'll know that there's something to initialize and so we'll call these set Quest step State method passing in that state then in any Quest steps we have we'll be forced to override that set Quest up State method so we'll do that for the collect coins Quest step and in there we'll just take that string State parse it back into an integer and then set it as the coins collected amount this could be a good thing to wrap an 8 try catch block as well if you wanted but I'm just going to leave it as is then since we're changing the state of the quest step by initializing it here we'll want the call that update State method to send out an event communicating that change to the rest of our system and next in the quest class we're going to add a new Constructor to create a West with some existing state data and we'll also do a bit of a defensive check in this Constructor since I can foresee a case where the quest steps State data length doesn't match the quest step prefab length during development for example if we're adding and removing Quest steps from a quest our save data will get out of sync and so if that happens we'll just log a warning to let ourselves know that we've changed things in the project and we need to reset our data because it's out of sync and then of course when we instantiate the current Quest step we'll need to pass in the state string for that step and now back in the quest manager We'll add a private method called load Quest that takes in a quest info scriptable object and there we'll Define a quest object as null and at the bottom we'll return that Quest object where in the middle we'll attempt to set that to something and then like we did with the save method We'll add a try catch block here with some error logging and in the try block we'll first check if that quest ID exists in player prefs and if it does we'll get the serialized string data using the quest ID as a key then deserialize that back into a quest data object using the Json utility.from Json method and last create a new Quest using the new Constructor that we created passing in all of these loaded Quest data and for the case where player prefs didn't contain that quest ID we'll just create a new Quest like we were doing before and that'll do it for the load method but we need to call it from somewhere in the createquest map method instead of creating a new Quest here we can call the load Quest method instead and there's one last thing we need to account for and that's that any Quest that's loaded in an in-progress State needs to have their current Quest step instantiated we don't want to do this as part of loading or creating the quest map since that gets called during the awake phase of the unity lifecycle and the quest step could potentially be sending out a state update event when we initialize it so we want to do that after all other classes have had a chance to subscribe to events so I think the best spot to do this is going to be in the start method of the quest manager and so we'll add a check here in this for each Loop where if the quest State on Startup is in progress will instantiate the current Quest step and just to make sure we're starting from a clean slate back in unity we'll go to edit clear all player perhaps to clear any previously saved data that we had and if we play the game start the coin collected Quest and collect two coins then exit the game and then we play the game again we'll see that all of the state has been persisted for that Quest by looking at the quest step that was instantiated and one last little quality of life thing we'll add for development is a way to turn off the persisted State and since we don't always want things persisting while we're doing development on the game so in the quest manager class We'll add a Boolean to the top called load Quest State and initialize that to be true then in the load method all we have to do is add that Boolean to this conditional track for when we create the quest and in doing so it'll always create a new Quest using this Constructor whenever the boolean's value is false and now back in Unity on the quest manager in the scene we can turn data persistence on or off with this checkbox and for the rest of this video we're going to leave it off and so that's it for the quest system itself but I wanted to include one last section for this video where we create another Quest just to cement how this system works and for that Quest we're going to have three Quest steps where each Quest step is visiting one of these pillars that means we'll need to visit this Northern pillar first and then this pillar here in the middle and then finally the southern pillar before we've completed the entire quest in the resources slash quests folder of the project create a new folder called visit pillars Quest and in there we'll create a new Quest info scriptable object and then fill out the values accordingly for this Quest the display name will be visit the pillars the level requirement will be 1 and for the quest prerequisites just for fun we'll actually add our collect coins Quest meaning that we'll need to complete that Quest before we're able to start this one and for rewards we'll do 200 gold and 1000 experience then we'll create a new c-sharp script in that same folder called visit pillars Quest step and in that script we can remove these placeholder methods and have it extend from the quest theft class instead of Model Behavior and we'll also add a require component annotation of type Circle collider 2D and of course because of how the quest step class is set up we'll need to override the set Quest step State method but for this Quest step in particular there isn't any state we need to keep track of so we'll just add a comment indicating that and then all we're going to do here is add an on trigger enter 2D method check for the player using the player tag since the player game object in the scene has that tag and if it's the player we'll simply finish the quest step and that's it for this Quest step next back in unity we'll create a new prefab call it visit first pillar Quest step and then attach the visit pillar Quest step script which should automatically add a circle collider 2D because of that require component annotation we added and we'll make sure to check the is trigger checkbox for that collider and next we're going to drag this into the scene and position it where we'd want it to be when it's instantiated right here seems like a good spot then we can copy those transform values over to the prefab itself and we'll also change the circle collider radius to be a bit bigger let's say 1.5 and next we'll duplicate the Prefab game object with Ctrl D on Windows rename it to visit second pillar Quest step find the position we want to instantiate that step at and copy those transform values over to that prefab as well and next you guessed it we're going to do the exact same thing for the third pillar calling the prefab visit third pillar Quest step and after we have those prefabs set up like we want to we can delete the game object from the scene and then we'll add those Quest steps to the quest info scriptable object making sure they're in the order that we want them to be in for the quest and that's going to be it for setting up the quest itself next in the scene we'll organize these Quest points by putting them under a couple of Parent Game objects and then we'll duplicate one of them change it to be both the start and finish point for this Quest switch out the quest info scriptable object for the visit pillars one and last position them over here in the scene and after all of that we can go in the play mode and check this out of course we can't start the quest yet because we added the collect coins Quest as a prerequisite so we'll have to finish that Quest first and if we go and do that we'll see that we can now start the visit pillars Quest so we'll start the quest and we'll notice that in the scene hierarchy the first Quest step has been instantiated and if we go and visit that pillar by entering the circle collider the quest step destroys itself and the next Quest step is instantiated for us to complete and of course we'll go visit that pillar as well in which case we move on to the third step and once we complete that third step the quest is ready to be finished which we can do by going back to the quest point where we'll turn that in and get our rewards and that's it for this video thank you so much for watching I know this was a really long tutorial so huge props to you if you made it to the end and I really hope you found this to be useful if you did please give the video a like and hit the Subscribe button if you'd like to see more from me this video in particular has been about two months in the making so letting me know if it was helpful or not is extremely appreciated and of course this system is just a foundation to build off of and customize for your own game however I am currently planning a couple of follow-up videos to this one to expand on the system a bit more which might already be out depending on when you're watching this one so check the description of the video if you're curious about that and also be sure to keep an eye on the pinned comment for this video if there are any crucial bugs that have been found after the video has been released I usually try to put solutions to those or extra information in the pinned comment and you're also welcome to come by my Discord server which is full of other people who are working on Creative projects or if you want to follow my personal project the path of Ren you can find me on Twitter Instagram or Tick Tock and the path of rent can also be wish listed on Steam if you think it looks interesting anyways thanks again for watching and I hope this was helpful [Music] thank you
Info
Channel: Shaped by Rain Studios
Views: 28,385
Rating: undefined out of 5
Keywords: trevermock, trevormock, trevor, trever, mock, quest system, unity quest system, unity tutorial, quest system save load, quest system save, quest system unity, unity making quests, rpg quest system, rpg quest system unity, rpg quests unity, rpg game quest system, how to create a quest system in unity, quest system with data persistence, unity rpg quest system, top down game quests, unity top down project, event driven architecture quest system
Id: UyTJLDGcT64
Channel Id: undefined
Length: 63min 58sec (3838 seconds)
Published: Sun Jun 04 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.