How to Make Race Missions
This page covers how to use the features of Quest and Mission Framework+ in Blueprints to make a race mission.
Setting up the data table:
Create a struct with the variables you will need for your race, in my race quest I have these variables.

Now, create a data table from this struct you just created and fill it out.

We will use this data table in various places to set rewards, maximum ring count, time remaining, description, quest name, and delegates. Your data table may look different if you need other data points.
Setting up the manager:
Each quest should have a manager to handle its operations. Let's create one for race missions.
Create a new folder in your content browser named "Manager". Ideally, this should be under a race quest folder.
Right click anywhere in the context browser and click "Blueprint Class" then select "Actor".

Once the new actor is made, name it something like "BP_RaceManager" and drag it into your level, you only need one of these in your level.
Now open up this manager actor.
In my game, races consist of two main parts: the rings that track progress and the timer that determines if the quest succeeds or fails. Both of these use quest delegates. Let's create two quest delegate bindings at the start of play so they can be called later. See screenshots in sections for when these are called.

With our delegates set up, we need to bind their callbacks to functions. I am using the create event node to remotely bind them to other custom events in my manager.
Binding the Timer Quest Delegate:
Input:
We bound a delegate at the beginning of play in our manager to start a timer. Here is the code
Countdown Timer Functionality
The start countdown timer is a simple function. It uses a data table to find the corresponding row by the current active quest name. I set the initial maximum time remaining from this data table. Then, I create a timer that counts down every second, reducing the time remaining. This time is stored as a quest progress named
CQ_RaceTimer
. I then check ifCQ_RaceTimer
is less than or equal to 0; if so, I cancel the quest.

This is the cancel challenge function called when the timer reaches 0:

Here is the reset ring progress function called above:

Binding the Ring Spawn Delegate:
We also bound a delegate at the beginning of our manager's play to spawn rings. Now, we need to create the function
This one is very simple function, it calls a multicast blueprint event dispatcher to the ring splines(next section), and spawns a widget that says "Quest Starting".
Setting up the Rings:
In my example, I am placing the rings along a spline, so I will be demonstrating spline math. You don't have to use splines if you don't want to make spawning easier.
Make two new actors, a BP_Ring and BP_RingSplineSpawner.
Setting up the Ring Spline Spawner:
In your BP_RingSplineSpawner or your Ring Spawner, make a function that uses the node get actor of class of your manager and binds the delegate we called for spawning rings,
On the event dispatcher callback from the manager, we want to get the current active quest and compare it to an instance-editable string variable that is set in the level editor when we design our race missions later.

if the quest we want is active then we want to spawn the ring along the spline, here is my code for it.
First half of the logic:

Second Half of the logic:

Setting up the rings to track progress:
You will want to add a shape and a box collider to this actor to begin. I used a torus from blender and a hologram material to make this.

- Event Overlap Handling for Box Collider
Upon event overlap of the box collider, execute this code. It checks if the quest name we passed to the spline spawner is active. If active, it increments the quest progress of the sub-quest that tracks ring progress. If the progress reaches the maximum amount from the data table, retrieve the quest manager and mark the quest completed through its complete function.

Here is the Get race challenge data pure function I am using for reference:

For the most part, we are finished. We just need to set up the quest giver/activator and integrate it into our level.
Setting up the Quest Giver/Activator:
Make an actor or character that has a mesh and a box collider to trigger things.
On your box collider overlap event, create a widget with accept, decline, and description UI elements added to it.

To pass the Quest Name variable, which should be instance-editable, from your quest giver/activator to the quest acceptance widget, you can do this by making the quest name variable instance-editable and setting it to expose on spawn in your widget.

In your widget blueprint, use these nodes on the accept button with the quest name you passed from your quest giver.
Once you click "Accept," your quest will start, and the rings will spawn. We still need to add these actors to our level, so let's do that.
Adding everything to our level:
Place the Quest Giver/Activator, the Quest Manager(only one manager per level), and the BP_RingSplineSpawner into your level where you want them.
Go to each one and fill out their instance editable variables to align with the data table, for example the Quest name should align with the row in the data table.
My Race Giver details panel:

My BP_RingSplineSpawner Details panel:

My data table where this race data is defined:

My level where the giver and spline spawner are placed:

How to quickly make more races:
Now that we built our race mission to be scalable, we can quickly add more races to our game. Here's how to do that:
Make a new data table entry for a new race, make sure you fill out its details to be unique from the others, and ensure its row name matches its quest name like the one above, "CQ_GroundRace_1".
Add another quest giver and spline spawner to your level and assign them the new quest name found in the data table row.
Thats it, the new quest should now work because we built this to be scalable.
Need support or have questions:
Join our discord and I will be happy to help you out with any questions or setting up different types of quests. https://discord.gg/GXZ7c74sTm
Last updated