The mission system

Bradamante is a story-heavy game, to manage progression through the game we are using a mission system inspired by Legend of Mana. This means a few things:

  • Only one mission at a time: No active side-missions, jobs, chores, etc… we want you to focus on one story beat at a time.
  • Missions can cross multiple maps: Our map is not “open world”, but we want you to be able to travel far and wide and many missions will require you to move from one place to another.
  • Keep your friends close: One feature I really want to implement is the possibility to pull other knights into your party for as many missions as possible. There will be a hub in the game when you’ll be able to talk to NPCs and recruit them to tag along. That means the game needs to be able to remember who’s in your party at all times.
  • Missions are geography-based: Most missions will activate and progress when the player reaches certain point in the map. Reaching a place will be a trigger for most missions.

Building Blocks

To explain our mission system, first I have to introduce a few concepts of Unreal’s base classes.

Player Controller:

At any time there’s a player interacting with the game, there is a Player Controller managing things under the hood. Customizing the Player Controller class is the first step to owning your game structure. Our PlayerController is responsible for handling the party, the playmodes, and the input. When a knight joins the party, it does so through the Player Controller.

At all times any script running can request a reference to the Player Controller and ask it anything.

But bear in mind, each time game changes to a new Level, the Player Controller unloads and reloads entirely. Any information the Player Controller had from the previous level is lost and needs to be rebuilt when the new level loads.

No, we are not using level streaming. Yes, I know this issue wouldn't happen if we were using level streaming.

A neat feature of the Player Controller is that you can actually create custom versions of it and override the default one for specific levels. So you can have weird levels with entirely different behaviours.

Default Pawn:

An Actor in Unreal is an object that exists in a level, has a physical presence and can manage events. A Pawn is a type of actor that can be controlled by a player or an AI.

Unreal allows us to define a “Default Pawn” that will spawn in the level automatically when the game starts, usually this is your player character, but not here, our Default Pawn is the camera manager. You as a player don’t really control one single character, but an entire party, and we want to be able to switch the party at any moment, change the main character at will, remove all members from the party during a scripted sequence, things like that.

This class also unloads completely when changing levels, which in our case is not really an issue because our camera manager won’t hold any information that needs to be remembered across levels. And this can also be overriden on specific levels, which allows for interesting “game changing” behaviours.

Game Instance

One instance to rule them all. This one is created when the game loads and remains active until the game closes completely. This is the class that can handle memory across levels.

In here I can place whatever logic and data I need when moving from one scene to the other.

Game Save

This is the class responsible for representing and storing data. At the time of writing this devlog I haven’t yet implemented this, but I already know what will go here.

Mission Handlers

I have a custom class for each mission, that handles the events for that particular story beat. In these handlers I can program scripted events to my heart’s content, to guide the pacing and story progression of each mission. When a new mission starts, the Game Instance verifies if the mission is still available (hasn’t been completed yet) and if it is, the appropiate handler is created. If the player moves to a different level, the Player Controller asks the Game Instance to rebuild the party and current mission, if any.

The process handling a mission is illustrated by this handy diagram:

In this quick video, a new mission is created when the player hits the New Game button. A new mission handler is created for the first mission of the game, and its first scripted event is loading the Abandoned Keep level where the mission starts. Notice how the characters move on their own for a brief period of time, some dialog, and then the player is given control.

This way of handling things allows us to create missions with very custom events, we can switch characters, start fights, interrupt things midway, trigger dialogues, trigger events by time or location, move characters around the map, ask the player to go to another location, etc… Almost whatever we want, we can do.