Search Unity

A Quest system architecture

Discussion in 'Scripting' started by Jihaysse, May 13, 2021.

  1. Jihaysse

    Jihaysse

    Joined:
    Mar 29, 2020
    Posts:
    53
    Hello everyone,

    I'm currently working on a quest system for my RPG game and I'm confused about which way I should go.

    I first thought about using Scriptable Objects and dividing the quests as such: QuestObject, QuestReward, QuestCondition.

    The issue I'm running into is that my "quest" is also a mot lore like "events" actually.
    For example, assume I'm making a Pokémon game and you have to choose between the 3 first Pokémons.

    So, the "quest" is triggered when you enter Professor Oak's laboratory, he comes walking to you, talks to you, then you have to choose between the 3 Pokéballs, then the quest is over, and the other Pokéball disappear.

    I thought it would be really hard to implement such thing using Scriptable Objects, as it differs a lot from another quest which might be "Kill 10 monsters and talk to <XY>".

    My other solution would then to use an IQuest interface (or an abstract class), which would require 3 functions: OnBeforeQuest, OnDuringQuest, OnQuestDone. Then, I'd create a class for each of my "non-generic" quests. This way, I can code each quest individually, which allows for more complexity, cutscenes or whatever...

    Let's retake the previous example:
    Consider a public class ChooseFirstPokemon : IQuest which will implement OnDuringQuest as the Professor Oak walking and talking to you, and OnQuestDone as adding the chosen Pokémon in your team.


    Isn't that a bit overkill to create a class for each non-generic quests?
    Of course, I can still use Scriptable Objects for generic quests.

    Do you have any other idea on how to implement such feature?

    Thank you for reading!

    Kind regards.
     
    Last edited: May 13, 2021
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,751
    You could probably tap into a firehose of interesting ways to go about this just by doing a few random quest tutorials on Youtube.

    Whatever you do I suggest starting as minimalistic as possible, proving the simplest most hard-wired quest pattern in and out so you have an API that the rest of the game can interact with.

    Once that API exists, you can start extending and expanding it.
     
  3. Jihaysse

    Jihaysse

    Joined:
    Mar 29, 2020
    Posts:
    53
    Thank you for your answer.
    I did research google and youtube but unfortunately all of these tutorials show how to implement a basic quest system (as I mentioned, kill X monsters, talk to XY, etc...). I can already do this without issue.

    My issue here is I don't know which way to go for more complex quests with a lot of events into it: some objects may be set inactive, some NPC will walk from one place to another, some other NPC will talk and say XY dialog, etc...
    I thought about using the Timeline feature, but I'm not sure it will be less overkill than actually making a class derived from a Quest abstract class for each quest.
     
  4. You're overthinking it... here is how it looks like from the outside, when you haven't thought about all day long:

    Super-simplistic dead-base implementation:
    Code (CSharp):
    1. private void Update()
    2. {
    3.     if (user.inventory.hasItem(item1)
    4.             || user.inventory.hasItem(item2)
    5.             || user.inventory.hasItem(item3))
    6.         this.quest.QuestFinished();
    7. }
    Now, it is wasteful and all, put a "Quest" on it, which can be fulfilled, put an inventory, which can provide if an item is in it.

    Next step:
    Same, but put some abstraction in place, add delegates to the inventory, so when the player get an item, other systems can get notification. Then the quest should register itself to these and check if any of its quest items picked up, if yes, step the quest ahead. If it's the finish, finish, play music, particle systems, dance moves.

    And so on, you can put more and more abstract things in place until your quest system is abstract enough to handle different kind of things and your other systems can provide real data through abstract notification-channels.

    Do not waste your time to fully plan out the entire system. You will never write it. Do the simple one. When you need something more, go back and put it in place, rewrite what you have if it is needed.
    Do not write a complex quest-stepping system with dialog choices and all before you really need it.
     
    bobisgod234 and Kurt-Dekker like this.
  5. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,347
    Lurking-Ninja and Kurt-Dekker have already done quite some work for you. If you try to take a step back and abstract then you will see that there are not many quest classes which you need to implement. Some examples (from the top of my head):

    Type 1) Acquire Resource(s), ends if the user acquires a resource (10 monster heads, one pokeball)
    Type 2) Wait for User interaction, ends if a button/key is pressed (proceed in dialog)
    Type 3) Wait for a defined game state (by handing over a function [think data type Predicate or Func]), this would be your most generic one, examples: wait for cutscene, wait for boss death, ... .
    Type 4) NestedQuest (actually all should be nestable, this would just be a list of quests and finishes if all subquest are done).
    Make an interface for start / update / stop / nesting.

    I think you will be hard pressed to find more. So there is no need for many quest classes. If you make a unique quest which can not be covered by your basic types (including nesting) then writing custom code is unavoidable. Don't wreck your brain trying to abstract everything.

    If you find yourself writing the same predicate multiple times then make it a new type. Quite often the logic variance is not that big (wait for cutscene is always the same) but the data is (many different cutscenes). Abstract the logic, parameterize the data and you will be fine.
     
    Last edited: May 14, 2021
    stain2319 and Kurt-Dekker like this.