Best approach for triggering and completing FPS Objectives

Hey everyone!

I am working on an FPS project and am looking for some guidance with setting up a standard objective system + the best way to store and reference that data.

I am currently working out of the Game Mode as a place to store all of the necessary data and have a test interface from an overlap volume to start new objectives. The interface is updating a struct in the Game Mode where I am storing text/an enum/and a ‘is completed?’ boolean. The UMG is updated based on this data. Should I be storing/incrementing this objective data differently? KVP/Map? Is it bad to use a volume with an interface for starting 1 objective now but later want to start or complete one based on another action? (on all enemies dead/player interacted with actor)

I also don’t know what the best approach is for keeping the chain of events clean and easily adjustable. It feels messy to have different conditions send an event back to the game mode to update the data. Is this where I would use an Event Dispatcher?

The functionality I am going for:

-Player reaches a new area/enters a volume/perfoms some action = trigger new ‘current objective’ -UMG is updated with appropriate text/marked enemy actor(s) or location to reach

-Player completes the objective by dealing with the enemies/interacts on actor(s)/reaches location -UMG is updated (Objective Complete!) objective data in game mode is updated and a new ‘current objective’ is triggered

  • New actors/location marked in the world…rinse and repeat.

First off, whatever you’re going to do, you need to put it in the save game. You can’t store progress in the game mode, it doesn’t exist in between games.

Exactly what you want to store and how it should be triggered is up to you really, but things you would typically keep a record of with this type of thing would include:

  • The name of the objective ( even the player can see this )
  • Has it started?
  • Has it ended?
  • % progess, maybe
  • Sub goals

You can use a structure for this, and keep it in the save game. You default save game would contain an array of these structures, with the setting:

  • Name of the objective
  • False
  • False
  • 0

You can use arbitrary events to trigger and end objectives, like trigger volume, moving a lever etc. You could also use blueprint interfaces ( the trigger talks to the player who in turns talks to the save game ), but I don’t think it would help.

I think it’s much easier to just code the trigger volume at the end of that corridor to change something in the save game.

Does that make sense?

Hey, thanks so much for responding. Yes, that makes sense for the most part. Just to make sure I understand correctly here is what I have.

-Create Save Game object in playerBP using ‘Create Save Game Object’
-In the Save Game, created a struct variable with the structure info
-In an actor…lets say lever. I have an event or interface that will trigger an event inside of the Save Game to update and set those values

Now here is where the noob in me comes out.
When you say ‘Your default save game would contain an array of these structures’ Do you mean the struct variable in the save game is an array or single variable?
Since splitting the struct isn’t an option as an array which would allow the lever event to update the values in the save game, how would I change and update the data for each individual array member?

As an array:


As a single variable:

Thanks for the help!!

You have actually tell the system ‘what shape’ a save game is:

Then inside:

It’s an array of structures, the struct is:

You make a save game if none exists when the game starts, and put stuff in there. It’s too much to type here, so here’s a quick vid on how the save game works:

So, when they hit that lever or enter that trigger volume, you can update the save game like this:

Which I know looks like a mouthful, but what you really do is put stuff in macros, so you can easily make tweaks to the save game. I know it seems a bit strange that you load it and then save it again, but it’s the best way, you can have a long meditate on that… :smiley:

I forgot to say, you define the objectives in the save game like this:

See how that grabs you. The only -slight- problem here is you need to know the array index of the objective to make the update. No biggie.

If you want me to show you anything about how to do the macros, or you wanna see what it looks like with dictionaries ( a structure that allows you to reference the save game stuff by name, rather than knowing the index ), just come back here and quote me :slight_smile:

Ah ha! The Get and Set array Elem was what I was looking for :slight_smile: Thank you. My thought was to have a “Start Objective” Macro and ‘Complete Objective’ that will handle a lot of what I need to do. I don’t mind using the index for the objectives but I would actually love to see how it works with the dictionaries and in a macro if thats not too much to ask.
I’ve used other engines but only as an LD doing standard mission scripting in AAA so some of this stuff is uncharted territory for me but things are clicking well :slight_smile:

Really, thank you so much for taking the time here!

Hi! ok…

So the way to do it with macros, is to make a macro library. That way, the macro is available anywhere…

So I made a library and just put the code I showed you earlier in and called it ‘StartObjective’ ( EDIT: I notice this code actually ends it, but you get the idea )

It’s just the same code, but I can pass in the objective id. Then when you want to use it, it’s just:

call.JPG

See, once you’ve got the macros working, it’s a piece of cake.


Dictionaries ( or map ) are declared like this:

So you see, you can use a string to look up the objectives, instead of a number.

Now the update code is:


It looks worse than it is. Basically you’re using the string ( name ) of the slot to find the right place and update it. With maps ( dictionaries ) you can’t have more than one slot with the same name, so the ADD node actually updates the one that’s already there.

NOTE: In both cases ( array and maps ), it’s good to read the save game, and copy back the bits you dont’ change along with the bit you do.

Then, to call this version in game or widget etc, you have:

call2.JPG

So, more intutitive to use, little bit more of a mare getting the macros straight.

Thank you so much for the explanation. I have it set up and running now and I can’t thank you enough, this is amazing :slight_smile:
https://forums.unrealengine.com/core/image/gif;base64

My last question- for updating the UMG, is there a way to get the “active objective” string so that the correct objective name is displayed. I know I could either set the string as a variable from the lever or player and grab it directly from there or call this function from the actor with the string so at least its in the same place as the macro that sets the string, but is there a better option to keep things more streamlined?

From my UMG:

You’re welcome.

Assuming only one is active:

map access.JPG

( that weird looking loop is what you get when you break open the pin ). For accessing maps you have:

ad25079e00aa95b918702f44bc1dcd917fb0ac53.jpeg

Ahh the Values…did not know about that node :slight_smile: Okay, so the branch is returning false even though the macro is switching the Started bool to true. I tested that the macro is setting the values correctly on the lever within the lever actor. Do i need to load in the save game within the UMG or is grabbing the reference from the player okay? In the playerBP the save game is created on Event Begin.

You have to actually read the information from the save game file, the reference doesn’t update as you update the save game the file does.

This is what I was saying… :>

Golden rules of using save games:

If you want to read something

get save game
cast to your save game
read value

If you want to write something

get save game
cast to your save game
read the structure you’re going to write to
update it, but also put the stuff back in you didn’t change
write the save game

You can use a reference to make your code neater, but the reference isn’t a live copy of the save game. It’s a file. That’s why we do it in macros. To avoid the hassle.

I know the write process looks a bit odd, but it you just write to the structure, you overwrite all the other parts you didn’t read first.

Tell me if you don’t get it ( but quote me, it’s just luck I saw this ).

Actually, yes, that reference IS a sort of live copy of the save game, but you will come unstuck if you start treating it like that.

Once things get really rolling you’ll have all sorts of proceses reading and writing to the save game. It’s too difficult to figure out who has the correct copy and when to write it out etc.

Much easier to do the whole operation in one go. ( open read update write - done ).

I kind of get it but don’t get how to do it- So the macro setup as you’ve shown- is reading and then writing the save game, but the reference we have in the UMG is just a ref not the updated saved game. Okay- so the macros have the necessary pieces/I don’t need to do anything more there, correct? So, in the UMG how do I read it?

get save game
cast to your save game (results as a "is already a ref you dont need to cast’ error)
read value - how ?

Thank you!

So to read the current object heading:

See it’s always the same process, not using a variable.

In use:

in use.JPG

Tell me how it goes… :slight_smile:

PS: Maybe I’m not being clear. You have to write a macro like this for EVERYTHING related to the save game. There’s not just 2 macros, one to get and one to put. You need a macro for all save game interaction. ( Well you don’t have to, but you’ll have a lot of messy code if you dont ).

Incidentally, another way of doing this is just a load of separate arrays. Still using a save game and macros, but you have a bunch of arrays:

  • Array of objective names
  • Array of bools ( started )
  • Array of bools ( completed )
  • Etc

The relationship between the arrays is just the index. So if you wanted to know the name of the active task, rumble down the bool array until you find which one is active, say it’s 6. Then you know the name of the task in the first array[6]. Like that.

It’s only if you find the working with dictionaries a bit cumbersome…

Thanks so much - loading in the UMG and reading the Dictionary values works perfect. I can’t thank you enough, seriously :slight_smile:

Excellent! - have fun :smiley: