Progress / Event System - How to approach?

Hi!,

I’m currently building a generic Progress System to track all that the player has done through multiple scenes and objects. I have an Event Manager and multiple Interactable Objects that upon interaction register a new Event.

Imagine that we have two different scenes with an NPC each. I can only speak to the NPC in scene 2 if I finish the conversation with the NPC in scene 1. What is the best way to do this?

Currently what I am doing is, when registering an event, saving also the Scene and Name of the object that I interacted with, and then when loading Scene2 I check if the object in Scene1 and with name X was interacted with, and hence unlock the conversation with the new NPC.

For one hand, for a large scale game, it seems that one needs to manually write all the unlocks and dependencies, so I can’t use a ‘randomly generated unique ID’, or something of the sorts. But using something so volatile such as Scene or Object name doesn’t seem ideal also. Is there any better solution? What am I missing? :slight_smile:

Thank you so much :wink:

Anyone? :slight_smile:

You’re probably not getting answers because the answer to this:

… is basically “it’s complicated.”

Every game has tons of ways of doing quests. Some hard-code it all. Some use LUA or another interpreted language to advance quest status. Some stub it out with little bits of scripts splattered all over the game assets and quests.

About the only advice I can offer is, take what you have now and identify an API that encapsulates everything:

For instance:

“Set X to be Y.”

“Check if X is Y?”

That way you can move ahead and learn where the true pain points come. What doesn’t scale? Do you have too many typos? Are you in danger of conflicting namespace, etc.

Go look at postmortems for games like Silent Hill (has like 7000 hard-coded booleans), or The Elder Scrolls games (strings and numbers, if I recall). Imagine how your game world would look with those systems, which certainly are in production shipping code today.

Kurt-Dekker,

Thank you so much for your answer. I was somewhat aware that ‘it’s complicated’ would be the answer. Setting and checking isn’t the problem atm (might be in the future), as the API already sends events, updates its own state and changes the state of other objects, either via a global scriptable object or local components that stores state dependencies.

My main issue relies on the ‘reference’ to each object, as I will need to read its status to change other objects’ state. And I always arrive at is that inevitably I will need to assign a manual key or value for a certain type (bool, string…) in each object to use it as a reference for these dependencies.
Arranging by Scene and ObjectName doesn’t seem to be the best way to go, as these can easily change, and then I would have to manually change all the dependencies that reference such object. I could assign a specific string to each object manually and never change it even if I change the object’s name… I was just wondering if there would be a safer way to do such :slight_smile:

Anyway, sorry for the rant, as it added nothing to the question posed nor it means to refute your answer. I will take a look at the postmortems you described and try to learn from actual large production games :wink:

Have a nice day.

Like Kurt-Dekker said… it gets complicated.

But you talked about the ObjectName business and I want to share a… weird? solution to it.

So my partner and I were making some games where we had this very similar problem. Instead of discussing with me… he instead came up with a really weird hack to do it exploiting the existing systems we already built (he’s the artist, i’m the programmer… he can’t write code at all).

Basically he used our inventory system.

See in our inventory system I had added a way to flag an inventory item as “invisible” so it didn’t show up in your inventory when you opened the inv screen (it also didn’t take up a slot). I don’t remember why it was added… but it was.

Anyways… our Inventory Items were represented by ScriptableObjects… meaning you don’t run into that string name dependency issue.

Lastly we had some simple scripts that could be called via our SPEvent system (basically its UnityEvent… just before UnityEvent was created by Unity, and with a few extra bells and whistles). Specifically 2 were:

i_AddInventory
i_TriggerIfInInventory

With these he’d add invisible inventory items to the player’s inventory to signal that some key event occurred. And then in other places if that inventory item was present he’d do actions based on that.

He later came with need for other i_'s that did things like “if any inventory item in inventory”, or “inventory item switch where it signals a specific SPEvent/UnityEvent based on which item was found first”, and other generic tools to facilitate even more robust scenarios.

The story wasn’t super large… so in the end there was really only like 20 invisible inventory tokens to represent all the states in the game. And it all “just worked” because it relied on an existing system (the inventory) that already had save funcitonality and inter scene tracking.

You will find most games use unique ID’s for every entity, as if they’re stored in a database. Look at Skyrim’s editor for inspiration. Adventure games are usually messier and just store global vars like HasPlayerSpokenToSteve because they have a limited amount of characters.

All you need to do is store your data seperately, and have the NPC from Scene2 read from the same data. DontDestroyOnLoad might be required, unless the data is static.

With that said… that’s not necessarily the approach I’d take.

Today we take a different approach. Explaining it isn’t exactly straight forward without going into a long diatribe.

But basically we have a thing we call the “ledger”, and all entities have uid’s (unique identifiers), and they can read and write values into the ledger tagged with their uid to store some state and share it amongst each other.

So if say a character in dungeon 1 cares if you talked to another character in the town… it just needs to know the uid of the town character and the parameter it cares about. So when you talk to in town character it flags said value in the ledger, and the dungeon character just sees if that value exists.

Note… we had this uid/ledger system in the games where my partner did the inv hack. We used it for general saving and the sort. He just didn’t know how to tap into it… and as is with anything team built… you sometimes just build things without discussing it and if it works… why fix it? I’ve since shown him how to use the more robust system.

Lordofduct and Stardog, thanks for your answers!
I might incorporate some of the stuff you said!

But in both your cases, you talk about UIDs. In lordofduct’s example of the characters in dungeon 1 and the town, I would need to know the UID of the character in the town and then manually write it somewhere (SO, in code, as a component…) for the character in dungeon 1 to get the event and change its state, right? Wouldn’t it be a bit confusing to know (and then read when debugging) the UIDs? Are you randomly generating a UID or manually attributing a human-readable UID for each object so you can use when you need its state to change another object?

Again, thanks ;D

That’s up to you what you want the UID to be.

You could make it human-readable, you could make it not. Heck you could use SO’s as uid’s if you really wanted to.

The critical thing is that they’re unique…