How to create a system for tracking player progress/events

Hi all!

TL;DR - What should I look at utilising within Unity for a quest-like event system.

Relatively beginner coder/Unity user here. Looking for some guidance on which system I should utilise in order to create a Quest like system.

A friend and I are working on a project that is predominantly just using uGUI.

The game has an adventure element to it, where if certain actions are performed, you progress into a new room, or maybe get prompted with a bit of text. Pretty standard fair. As a basic mechanic of progression though, players move through rooms.

I’m at the point where I need to incorporate some sort of system for progressing players in the game, and what actions this creates accordingly.

So some base requirements:

  • Based on events/triggers, perform actions.
  • Track player progress through these events/triggers.
  • Be able to jump to an event. Say you start at event 0, but you want to test event 34. Be able to jump to that via a debug command or similiar (keypress maybe, doesn’t matter).

An example scenario might be:

  • Player starts off in a dark room. If either a) 20 seconds elapses, and/or b) the player clicks anywhere on the screen, text should appear in a given area. Super basic scenario. This could branch out to other things happening in the room obviously.

Now, I have looked into a few different ways to approach an event/trigger system like this, that is both scale-able for crazier events, and also easy to manage and work with as the story grows and the events potentially get bigger or even change due to testing.

**1) Unity EventSystem OR C# Delegations **
This could be great, as I would be able to have actions announce themselves, and then have the respective objects listen for these events, and perform the relevant actions.
The big problem with this, is I don’t see it being easy to track events on a “this event has fired” level. If things changed, the listeners etc would need to be altered. This might be a nightmare for management overhead.
Given the example scenario, my thoughts so far have been:
Player clicks → Announce this event → Text box (or controller) is listening, and shows the text.
20 seconds elapses → Announce this event → Text box (or controller) is listening, and shows the text.

**2) Mecanim Animation System as a Finite State Machine **
This is currently striking me as the better method to use. I could implement a node structure using Mecanim, except it wouldn’t be handling animations as it was initially intended to do. I’d instead be using it purely as a Finite State Machine, for tracking events, and the states that they are in.
Given the example scenario, my thoughts so far have been:
Clicking, or 20 seconds passing, moves to the “Show text” State. This then has a script associated with it that does all the relevant work.
My thoughts were having a sub-state machine per room, that then has all the states of that various room contained in it.

**3) A crazy class full of if statements and bools **
A controller like class that simply tracks a giant list of bools to say whether an event has triggered or not.
Given the example scenario, my thoughts so far have been:
Clicking within the first 20 seconds of play triggers the text to show, and the bool to be switched.

What I am actually prodding for here, is what would you all suggest as something I should use? The node system of Mecanim appeals to me a lot, and makes sense with how big the structure could get - very easy to visualise and make changes. But is it a good option? Is there something better for this scenario? Am I talking smack and the obvious answer is X?
Thanks all!


Edit: Formatting.

Addressing architecture up front, nice.

I only read through your questions once, and there’s a lot to consider, and my experience is that 3 different developers could each pick one of the three solutions you’ve proposed/considered, and each could make great arguments for and against each solution. With that, my $0.02 is on solution 3, crazy class. But, you don’t have to use switches/if/else’s, you can just using mapping and reflection, or some type of bitwise reference (if there’s not a lot of “states”).

The arguments for 3 are this.

  1. Performance, even if you had 100 switches in the “crazy” class, it’d likely be faster (outperform) all of the other solutions. It may only be by a few hundred milliseconds, but in an Update, and especally a FixedUpdate, the milliseconds will become very noticable.
  2. Maintenance, one class, multiple developers (even if it’s just two of you). Having one crazy class will greatly simplify your concurrent development and force you guys/girls to synchronize on that one class. It will essentially force you to “get on the same page” and stay there in terms of development, which is great thing. With Visual Studio and code comments/regions, you can each “own”, edit and master one class with core logic.
  3. Problems with the alternatives. Solution 1, forget about catching and tracking exceptions when one of your listeners erorrs out, sure you can see the stack trace, but then what, no way to deal with the exception. Another ding against Solution 1, memory leaks. You’re going to have to put a lot of effort into ensuring the listeners are added and removed timely, and properly. A constant maintenance headache. Solution 2, that’s actually a cool idea, i hate to ding it just because it’s such a novel approach. But, the dings are, again, maintenance. Because now you’ve just added a huge amount of code that’s going to be executing in support of mechanim. You won’t be maintaining that code, but you’ll be stuck with all the memory and cpu resources that Unity will grab to instantiate Animator components and state machines. And, if you’re ok with this, you’ll also have to consider the performance hits whenever you update the “state” by setting parameters. Making things confusing, you may have have a UI edit point in addition to setting the state in the code. At first that could seem appealing because you’d have a UI view to your “state” settings, but if you then use that UI to make config changes, that’s going to ripple through the code and become a synching/versioning challenge

In a nutshell, solution 3, the crazy class is the simpler, better-performing, more-maintainable lesser evil.