Welcome to our talk so delivering a wide scope of features and content in Claire's here expedition 33 when you have only four programmers. It's a mouthful but I yeah. Um so we are really happy to be here at GDC and we are just out from BIOS update and Windows update on the laptop so yeah. A bit stressed out but hopefully it will turn okay. Um so first of all bonjour mes amis. I'm Tom Gimond one of the co-founders of Symphony Interactive and on expedition 33 I was technical director and lead programmer and today I'm joined by Florian on stage.
Hello. I'm Francois. I'm senior gameplay programmer at Sunfall for the past three years. Let's talk a bit about expedition 43. I will be fast. This is what we call an interactive turn-based game. I will come to that on the next slide. It was released last year 24th of April on major platforms so PC, PS5 and Xbox series. Published by our friends from Kepler
Interactive and developed by quite a small team around 30 to 40 people for the core dev team at Sunfall with some external partners especially for the QA, localization, porting, animation and music. So what's expedition 33? Well, we divide the game in two parts. The first one will be the exploration part because we have a semi-open areas that are quite linear and that are connected with an open world namely the world map. We have some let's say modern features so a third-person camera. You can jump, you can climb, you can run, you have a vehicle as a skier on the world map, you can attack the enemies in the world stuff like that. And also we have a strong focus on narration meaning that we have a lot of stuff regarding cutscenes, dialogues, level scripting and game flow. On the other side of the game you will find what we call the reactive turn base system. So, this is a turn base combat system innovated
from old RPGs, but we twist it with some real time mechanics like parrying and dodging. And this is where you will find most of the contents that you will find in any regular RPGs because here in the combat we will have a wide varieties of enemies, characters, statistics, passive effects, and so on.
Okay, so the agenda for today, we're going to go talk a little bit about the project history and the technical context context in which it was made to understand how we achieved with only four programmers delivering this game and we will more go more in-depth into how we give power to our game designers and take two big examples with the battle system and also how we created the world map which are two key parts of the game. So, first of all, yeah, this is not meant to be like a a tutorial or like a an absolute guide on how to make games. This is more of a retrospective on how
we made Expedition Zero History. So, it's an Unreal Engine oriented. Not everything you will see here is best is best practice. So, yeah. Take that with a a grain of salt.
[snorts]
So, to begin with, the project history and the size of the team. So, at the very beginning it was like a personal project from Guillaume Boucher, creative director, and I was helping him, you know, from time to time. So, yeah, let's consider the size of the team versus the size of the programming team. So, at that stage, it was the two of us, but I was yeah, spending only half my time working on that project. So, let's count 0.5. Later on, we started working on bigger prototypes and the team started to grow a little bit more. So, at this point I was like the single the only programmer in the team. Then we went on to create uh other prototypes to do a fundraising. Um so the team grew bigger, we had, you know, like a writing composer, art director, producer joining joining in.
Uh but I was still the only programmer in the team back then. Um so with the fundraising money, we worked on the vertical slice for a year. So the team grew much bigger to 12 people. And yet, I was still only programmer uh in the team somehow. Um so at the end of the vertical slice, uh so we went to the GDC 2022, and we met with Kepler Interactive. Uh we fell in love, we signed a deal, it was amazing. Um that allowed us uh to grow the team much bigger. Um and uh yeah, we learned from our mistakes, and the programming team grew yeah, enormously, up to three people. So with Flo here with me today, uh and Jerome, our engine programmer, which was yeah, very very useful addition to to the team. So we were working at on the alpha uh at this stage. Uh we went on to work uh on the beta with just one more person joining the team. A programmer, yay!
[laughter]
Working on the UI, which is of course very important for a game for an RPG. You have a lot of menus uh to to work on. And uh by the closing of the game, the final phase, uh so there were still the four of us, uh but we also got excellent help both from uh Kepler Interactive, so two programmers from there, and also uh Apsolon, um a Kepler studio working on their own game, and also doing some porting work. Uh so two other programmers uh helping us basically yeah, ship uh ship the game. So overall, the technical context uh during the whole of production, we always had limited programming bandwidth, meaning we had to focus on what was more as the most important uh both for the player, but also for the team working on the project. So, this is a very quick survival guide for small programming teams like ours and some yeah, key learnings that we took along the way. So, we are using Unreal Engine. It's getting quite frequent
updates and we had this philosophy of updating the engine as we wait as we went, sorry. And we we kept updating the engine until pretty quite late in production because it's bringing bringing so much, you know, so many bug fixes, performance optimizations. Earlier in production we had new features also coming coming in with Unreal 5. So, yeah, it's a lot of integration work. But yeah, overall I would say it's it's worth it. Second tip is don't make an engine in the engine because it will make the first point much harder to do when you did yeah, big changes to the engine. Most of the time on the next update you have to reconcile that which can be quite time-consuming and as yeah, programmer in a small team you want to be delivering feature to the players and to the rest of the team and not spend too long updating the engine whenever a new version comes out. So, overall we have a very a very
vanilla first approach. We take all the tools that are natively present in the engine and we push them to their to their limits so we don't have to develop bespoke tools for the features and the content of the of the game. And maybe yeah, another interesting point I think to mention is that we maybe more early in the production, but we did experiment a lot with plugins because it's a great way to yeah, experiment and see what you like, what you don't and keep it as an internal feature afterward or not. So, regarding the plugins we actually use, so there are quite a few bundled with Unreal which are not all activated by default. The most important one for us were common UI for the UI system, meta sound, geometry scripting, and also some rendering feature features with water and hair strand hair strands plugin. But we also had external plugins, so basically from the marketplace. We use advanced locomotion system for
the three C's of our characters. For UI, we had different plugins to add a few nice touches like Niagara UI to have Niagara particles inside your menus to give some life. We have LGUI for the 3D battle wheel also. And we also have some animation plugins to add you know secondary movement or IK solving all those kind of features.
So, you could ask how do we write game game play C++ code in Unreal with only four of us? And we have a short answer to that is that we don't. Because a 95% around of the core game play logic of Expedition 33 is entirely in blueprint. Yes, so we use blueprints for all our game play systems. And as Tom said, the goal is to use as much as we can native engine features and tools. So, this is a good way to achieve that. It also means that we follow all the
Unreal paradigm as much as we can for absolutely all our features because this way we get new we get features and tools for free. So, we rely a lot on the property system, on the actor component system, the data tables, the data assets, the behavior trees, we use a lot the sequencer in battle, and so on. Of course, that comes with a lot of advantages and disadvantages and I will go through them right now. The first one, and this is super valuable, is that you have a super nice stability because in blueprints when you encounter a logic error, you don't crash compared to C++. And as a small team, this is super valuable because uh we don't want anyone to be blocked in the morning and waiting for a programmer to fix some stuff in C++. Um, that's super nice. Also, as programmers, we don't need to deal with the compilation, with engine rebooting, with auto reloading, and stuff like that. Um, and of course in the end uh it's uh short a lot the iteration times when we use the editor. Usually, you start the editor in the morning, you close it uh by the end of the day.
Uh we also have a good discoverability of new features and API in the engine uh because in blueprints uh you have a super nice auto complete that is robust, exhaustive, uh takes care of the parameters, and stuff like that. And um when you need to dig into uh new system to learn how it's working, uh usually in code you will uh need to dig a lot in classes, uh all the internal stuff, understand uh understanding okay, what's uh useful for the user, what the need what is only useful for the internal system, stuff like that. In blueprints, you only have the subset uh of methods that are only useful for the users. So, it helps a lot when when you need to understand new systems and features. And finally, uh using blueprints allows us to give all workload to the designers. Um, we want everybody to contribute to the engine, and the blueprints allows us to do that. Um, and yeah, all game designers, artist team, sound team, and so on works with blueprints to help us to make the game because we cannot do it by ourselves.
So, this is also a good advantage that everything is accessible by everyone. Let's have a look at the cons of using blueprints. And the first one is that everything is accessible by everyone. Uh that's super nice, but also it means that as programmers, you need to uh safeguard everything. You need to make sure that uh critical systems continue to work even um if all our team members um well, change blueprints. Um, it also facilitates bad memory practices. Uh Uh most common one in blueprints is that when you try to cast um an object into another one, you will automatically load the full class in memory even if the cast fail. Uh so, you need to be really careful about references and stuff like that. Uh also, uh blueprints are binary, meaning that when you need to deal with versioning systems, well, it's a pain. Uh usually, two programmers cannot work at the same time on the same blueprint because you have exclusive lock on it. And same thing if you are working in parallel branches, you need to make sure that um not not a lot of people are
working together on the same blueprints because you cannot merge them. Also, uh this is much harder to debug blueprints uh when you are only uh testing a package build. Um usually, you can still have access to the blueprints call stack in um in package build, but it's not as convenient as the editor. Uh the good thing is that normally, you should have like the same behavior between the editor and the package build, but well, sometimes um stuff happens. Uh so, yeah, this is much harder uh to debug. And you have two solutions to solve that. Uh no, you don't have two solutions. You have one issue to solve before. Uh you need to repackage everything. So, when you need to uh test a new blueprint, uh or you need to test something that is only working in package build, you need to repackage everything because everything is binary. Uh and basically, the time that you gain uh in the editor when working with blueprints, you kind of lose it when you because you need to package a lot of time. So, you have two solutions uh to this issue. The first one will be to um do
lightweight builds, so a smaller builds, cleaning your references, do devops support, and so on. And we are only four of us, so we decided to go the other way. And basically, that's building a build farm uh to well, reduce the reduce the the packaging time. And with a good build farm, we reduce our time from 2 and 1/2 an hour to 50 minutes uh for full packaging the game. You could also ask, uh but you do write code at some point, right? Because yeah, we are programmers in the end and yes, we do the remaining 5%. Um we have some common cases. The first one is that usually we need to expose more stuff in blueprints uh by creating accessors and helpers. And after that, we can even we can create even more blueprints afterwards. That's amazing. Uh we have of course some platform and vendor specific code for store management, the login of the user, for example. We also use um some experimental features that are not yet available in blueprints. So, in this case, we only use pure C++ code to manage these features. Oops, sorry.
Uh also a bit of optimization. Sometimes blueprints the the well, do not run um fast enough. So, we move this code uh directly in C++. And finally, a bit of devops scripting to help build the building the engine.
Okay, so now the overall question that remains is is it still too much work for uh four programmers only? Uh and if we look a little bit at the stack to build a game. So, of course, you have your core logic and on top of that, you have the game content. So, first of all, uh at Sandfall, the game content is only and exclusively made by uh technical designers, uh all designers overall. Uh as programmers, we never have to create uh anything new when it comes to content. Um so, this relies on a stack of uh engine features. On top of that, we have the gameplay systems that we built. And on top of that, you have the data tools and the scripting APIs uh to be used by the designers to create uh content. So, if we take a deeper look at how what we
actually have to build um in this stack. So, the engine features, you have all the core features uh of Unreal, the major update, uh the plugins. It's yeah, coming out of the box uh or externally from the marketplace. Uh we have this yeah, tiny green uh part which is made by us, which is the engine the engine changes. Keep them small. Um we also have the platform support. Uh, so for the various uh, consoles, uh, for example, which comes natively with Unreal, but you do have to implement the TRCs. So, as Fran said, like user authentication, uh, achievement integration. Uh, we did some of them and we also had some external help in blue from Kepler and App Software. So, we also have the So, we have the game systems, which is like the core uh, of our work as programmers. Uh, we sometimes use plugin, as I mentioned before, to prototype a feature. Um, for example, for example, like the document reading uh, feature, like when you find the expression journals, at
first it was like a plugin and then we completely redid the this feature from scratch. But, it's a nice way to see how
[snorts]
like a a gameplay element fits in the whole game. When it comes to, yeah, providing tools to the designers, uh, so you have a lot of stuff coming in for free uh, when you use Unreal. Um, but you do have to provide some kind of API, for example, in blueprints um, or with declarative data tools. Um, but yeah, you don't have to create actual tool uh, yourself. It's more of a creating an API either through data or through blueprint. And now that gives, yeah, the freedom to the designers to create lots of asset, lots of blueprint, and that's all the content of the game. So, in the end for us, it is all mostly game systems and the game scripting API um, that takes uh, the most time.
Yeah, so as Fran said, um, well, our designers will be involved to creating the contents by doing assets
and blueprints. And as programmers, our job is to help them to do that. So, let's dig into it and let's talk about how we uh, well, share as programmers our best practices to create code and how we help them to do to do that. So, uh, the first thing uh, that we did, uh, is to, well, create API and for example here, uh, you have some nodes that comes from our progression state API. Basically, designers can access everything in the game. That could be the quest, uh, status of an objective, uh, which character is in the team, checking in the inventory, uh, the number of times the game has been done, if we're in new game players, uh, cinematic, battle, enemies, everything is accessible uh, with simple nodes. And the main point here is that designers don't need to know um, how to access the quest system, how to uh, how to how do I access uh, the inventory? Everything is uh, directly available um, with simple nodes. So, that helps us uh, that helped them a lot when they need to uh, create content.
We also uh, created some useful nodes and well, let's say simpler nodes um, for common patterns that are quite error-prone. Um, the a simple example will be what you see in the blue nodes. Um, when sometimes you need to create like asynchronous in blueprints, you can do that by using events. Uh, but it's not super convenient for the designers and really error-prone. So, we encapsulate that in simpler nodes, uh, something that we call the latent action. Uh, basically, you have a blueprint that computes something. Uh, this blueprint will start a latent action and any of our blueprints can wait with a simple node for the end of execution of this previous blueprint and this way our designers can manage kind of asynchronous blueprints without having to deal uh, with uh, difficult pattern. Also, as I said, we help them, uh, we encourage them with best practices. Uh, we did some um, slight modifications to the engines to achieve that only mainly in the UX. Uh, the first thing, uh, we changed the way the variables in blueprints are working by default um,
and we'll put all variables in public and we just changed that so everything is private by default. Uh, of course, it helps our designers to encapsulate um, the variables um, and it avoids accidental manipulation from outside and if they want to access something from outside, they create an access or asset or if they need to. And also as programmers, if we need to refactor a system, it helps us a lot to know if something has leaked or not outside the blueprints. We also just changed the way duplicated variables and functions are displayed. We just put them in dark red and that's pretty sufficient to say to a designer, "Hey, don't use that." And also obviously the best way to share best practices is to talk to them to well, to give them the experience that we have. So we do regular peer review and approval for certain and critical systems. Also, for each system that we developed, we have a focus on putting a good debug display to it. This debug display is mandatory for each system we develop
and also this debug display should be available while playing. The goal here is that when a designer will work on a feature, they can directly see what's happening in the system and so that that helps us them a lot when they need to debug or to fix something. For example here, you have the gameplay elements system that we developed and you can see directly the communication between the actors in the game.
So yeah, providing helpers is a big part of giving autonomy to the designers, but another part which is as important is when they have created content, it's important to help them find and resolve issue in autonomy. And one tool for that is validation. So at any time you have the possibility in Unreal to run validation script that will make sure that the content, you know, will work as it is intended by the designer. So that's a native feature in the Unreal. You do have some nice support with notification and some some logs appearing. So the way it works is that we define
validator. It can runs on data tables, data asset, it, blueprint classes. Um, and it's just like, yeah, basically extending a base class provided by Unreal and we can, of course, define validators in blueprint as well because most of our systems are in blueprint. So, in terms of namespace, we only have access to the blueprint namespace for from a blueprint validator. Uh, we did a special helper for actor classes because it's very hard to validate the structure of an actor class from its definition. So, we actually have a special validator that creates that spawns an intransient instance of that actor class so we can properly inspect it during the validation process, in particular when it comes to the hierarchy of components and stuff like that. So, we have another validation for actor instances. So, for example, you want sometimes to make sure that an actor is not clipping through another or
that the collision flags are properly set up. So, we created like a blueprint interface that can be implemented by both actors and components. And on every single instance of those actor and component in the level, it will run the validation script. It can make sure that each individual usage of the actor or the component matches the rules you've set. Um, [snorts] yeah.
Uh, so validation is one part of the problem. The other part is how do we feedback errors to the designers when they are testing the game. And here is a bit of an history of our error feedbacking. Uh, at first, we decided to put a string in error mode. That's nice. You can see there it's in the red so you cannot miss it, right? When you look at the logs. Uh, well, that's the whole point. It's only when you look at the logs. So, of course, that was not enough so we dropped that idea to have something more visible. Um, the second part is that we show it
on the screen. That's amazing because you have it there. So, when the designer will play the game or play in the editor, he will see instantly the error. But, you know, over designers are working and over designers are setting up stuff. So, usually you have more something like that. And sadly, you new error that is just just appeared here, well, you missed it because it's in the bench of our errors. So, what's next? Well, we decided to be a pain for our designers and welcoming a blocking pop-up. Um so, yeah. We decided to ensure visibility for critical issues to have a blocking pop-up to feedback all the errors. This is of course mainly triggers for our gameplay errors that are important to say to designers, "Hey, this system cannot work anymore. You need to fix your stuff." This is enabled by default for the whole team. So, the art the art team, for example, because it helps a lot to find out to reproduce issues. This is
also available for the QA team. So, they have visibility to this pop-up and they can report this pop-ups. Also, this is working for all platforms. So, in your PS5, you have a superb 4K pop-up in full screen. And if you are working in the editor, you have a retry button. And if you press this retry button, the game will automatically pause and you can access the call stack of the blueprints, the variables, and see directly where the error is happening and try to fix it. A lot of questions, of course. Do not spam your team because this is a blocking pop-up and a word of apology, sorry team.
Okay, so now we're going to deep dive into how we actually create content for the game following like this philosophy starting with the battle system. So, yeah, a quick reminder. So, this is a turn-based structure inspired by JRPGs. You have three heroes versus up to three enemies. Our battle locations are predetermined and placed uh in the levels. Um, our
twist is that we have real-time parrying and dodging uh and you can of course land a super juicy counter attack uh if you succeed to parry everything. Um, so the battle map layout itself. So, it's a an actor that we drop in the levels. Um, it's pretty straightforward. Uh, we have two systems. So, the first one is for placing enemies. Um, we have two systems working here. Uh, the first one is the white spline that you can see on the screen here. Um, it allows for dynamic positioning of enemies based on their sizes. Uh, so it's uh yeah, very very dynamic, very flexible. Uh, but we also have uh in red uh hardcoded spots that we usually use uh for scripted battles when we know in advance what type of enemies we're going to yeah, are going to be there. Um, and when it comes to boss battles where uh we usually have more exotic uh level design setups. And when it comes to the heroes, we only have the hardcoded spots uh system and the idea that you can of course adjust uh the spots to the to the actual environment location you are you
are in. So, if we take a look at the structure of like one turn in the battle system, so you begin with uh the turn start events. So, I put the purple diamond in there because like it means that it's a scripting opportunity for designers to create content. So, a good example of that are uh for example the status the status effect of fire and stun and stun, sorry, which pauses the game to show some animation, some feedback uh to the player. Um, you can do a lot of things during this turn start phase. Then we enter the actual turn body. Um, if it's the turn uh of the player of a hero, you will have of course uh the skill menu and then the actual hero action that the player has chosen. This is fully scripted uh by the designers. We will get to that a bit later. Um, and when it comes to enemies, the enemy attacks and just like choosing patterns, uh it's fully implemented by uh the designers as well. Uh it can yeah, uh trigger a counter attack, as I said before. If you parry everything, um after that, you
have what we call enemy last 10 moves. So, it's another scripting opportunities for certain enemies to perform one last action before they actually die. Um so, here we have uh Luned killing the Sakapatats, and it will come uh crashing into the team to try to inflict uh damage just before dying. So, after that, we have uh another scripting opportunity, which we call turn end event. So, it allows to react to what has has happened uh during that turn. So, here we have the Brazeler, and it will he will react to uh the elemental affinity uh of the skill that you've used on him. So, it will it will switch between fire and ice uh and ice elemental affinity uh based on what skill you use. And of course, uh the turn end event can, you know, trigger uh an attack on the team, which can be counter-attacked uh once again. So, we have some enemies that have this kind of loop, uh including the Brazeler.
Uh and then at the end, um we have like several uh elements in the game that allow your character to automatically resurrect uh at some point. Uh so, this is the moment when we make sure that yeah, all pending resurrects have been uh processed. Uh so, you can actually end up with your whole team dead for a for a bit before you actually have the auto resurrect uh kicking in. So, when it comes to creating skills for uh for heroes, um every skill it's a blueprint asset, so it it's a class. Um we have common uh entry points uh of events and functions. Um so, we have the execution entry point, uh and basically, yeah, 90% of the skills uh in Expressions of Wrath 3 are uh a level sequence. So, linear content, so we made a helper to uh to help designers with that. So, you set up which sequence you actually want to play and it will play with the right, you know, set up, the snapping, all of that automatically. And
then you have another function called the action effect and it allows us to script what is going on in terms of gameplay during that sequence. So, you will have specific keyframe triggering that function in the skill script. And for example, here is this is this is a full setup for the marking shot skill on the blueprint side. So, you have only the level sequence being started and then you have the action effect that computes damages based on the QTEs that the player failed or or succeed. And then we deal damage based on that and we also apply the marked status effect at the end. So, here is an example of the level sequencer that we that we use for skills. So, this is Luna's skill, Elemental Genesis. So, this is how it looks in game and this is how it looks in preview in the editor. So, you can see you we have a lot of, you know, just floating actors in the scene that are used by the projectile system. But overall, we have
a very good idea of what is going on in terms of visual rhythm, animation, etc. And we can see on the right the actual timeline for that skill and we will dive into that just now. So, so yeah, we have a lot going on. First of all, we have the actual character. So, it is set up in the sequence itself, but it has a dynamic tag which allows us to bind this character from the sequence to the character which already exists in the scene from the gameplay. We then have an important part which is the animations of the character. And I think this one in particular shows quite well how we work at Sunfall it comes to animation. We stitch a lot of animations together. We blend them together. We reuse a lot of stuff. But the point here is that the designers really have freedom to use, you know, this whole library of animations of each character to create, you know, new new skills from
that. We also have, of course, the battle keyframes system, which I mentioned a bit before. So, it manages the action effect call function call to the function of action effects. And it also automatically manages the QT. So, you will have automatic, you know, UI kicking in as soon as you put one keyframe of that type in the in the sequence. We have, of course, the camera, which is super important for like all the art direction part. So, we animate a lot of Sorry. We animate a lot of, of course, of course, movement, but also focal length. We have a lot of camera shakes to give, you know, intense intensity to the action. We have, of course, a lot of Naraka system going on. So, it's great to be able to, you know, preview all of that right at the editing phase. We have time dilation, which we use quite a lot. It's also super useful to, you know, give strong impact to to the action and also help the player anticipate a little bit
when the enemy is attacking, for example, when the hits are going to land. And we also have, of course, the audio systems. So, it's mostly events calling functions. So, here you can see in the director blueprint all those keyframes are actually calling blueprint functions. The The system is not perfect because from the timeline you cannot see exactly what is being called or with which parameters. But yeah, we didn't have the time to create, you know, a big a bespoke sound track timeline track for that system. So, yeah, it's one of the limitations of using your only vanilla systems. When it comes to movement, for the Elemental Genesis skill, it's pretty straightforward because it's a ranged skill. So, Luned will only, you know, fly around a little bit from her position, but sometimes we have more complex layouts and based on where the character is starting and reaching for close quarter skills, you can have very different travel
distances. So, our approach to that is that the starting and end locations are set by the script of the skill itself. And then it's just a floating point track from zero to one to control the progress along that vector. And the core idea is that as we have real-time dodging and parrying, it's important to have consistent timing regardless of the distance the character or the enemy has to to to walk towards you. Okay, so another important system that we use a lot are battle buffs. So, it's one of the first system systems we actually created in the battle system and the core concept is really simple. The idea is that you attach a blueprint script to a given character for a given number of turns. And then you can change a lot of the battle behavior from that particular script. So, common examples are of course status effects of burn, stun, etc. All the passive effects coming from the equipment are made using that system. So, yeah, if
you played the game, you know how crazy it can alter the gameplay. So, this is all using this this buff system. We have of course hero's unique mechanic which can also modify quite a lot how the character plays plays, sorry. And we also have of course enemy unique behaviors implemented using that because it's just like a great toolbox. And we also use it for simpler use cases like markers and cool downs, for example. So, how we create a buff is quite similar to skills, actually. We have a base class from which we inherit. Um we have activation and deactivation uh events, so when the buffs is applied and when it dissipates. Um and we also have a lot of gameplay hooks. So, functions that designers can override um to alter a lot of things that are happening in the gameplay, so you can modify damages, you can, you know, trigger new things happening from damages, you can trigger uh healing um healing action, or you can give APs
action points uh back to the character. So, it's really like, yeah, many building blocks um and we have also different phases of damage calculation uh that allows, you know, that gives designers great flexibility when it comes to modifying uh damage. And of course, as everything is blueprint, you can still bind to existing event dispatchers uh from all the systems uh that already exists. Uh yeah. Uh so, yeah, here is a quick example of uh the stun and the burn um buffs. Uh they're literally pausing the flow of the turn, so we can we have the time to actually give feedback to the to the player of what is uh going on. So, this system is like really strong and we can do a lot of things uh with it. So, to recap, uh yeah, blueprints and sequencers are already the foundation of how we build uh content for the battle system. Um we try to keep in mind that, yeah, as you can modify a lot of things with buffs, uh everything has to be modifier friendly,
so no direct assigning of values. You always have You always have to keep track of what you are modifying, so you can remove it uh later later. Uh we created standard entry points for the designers uh to use, so they have, you know, maximum freedom uh to create content. Um and yeah, we keep this idea of keeping it as simple as possible, but yet as everything is blueprint, uh if they want to deep dive uh into a more complex uh features themselves, they can also uh they can also do it. So, if we take a look at yeah, the development stack here. So, engine features blueprint and sequencer, we don't have to do it. It's native in Unreal. We mostly work on the battle systems. We have to develop the the blueprint API for that, but then it's on the designers to actually create the skill logic and the skill sequences. Okay, let's move to
uh the final part. Uh so, here the goal is to explain to you how we build our world map. And also, this way we can
explain a bit of philosophy when you need to create completely new stuff uh in the game. So, let's start with the final result. Uh so, this is the world map. Uh you can uh walk as a characters on the as a character, sorry, on the world map. At some point, we will unlock Enki, that is a vehicle. Uh and that can run much faster on the world map. Um at some point in the game, you will also unlock uh the uh way to swim and to uh traverse the continents and go to uh islands and stuff like that. And by the end uh also of the game, you will have the opportunity to uh fly directly with Enki. So, you can move absolutely everywhere. Um and go much faster in the world map. You can land on any workable or swimmable area. And at any point, of course, you can turn back into a character and move again to loot, interact, and battle the enemies. So, this is what we are targeting to do. Uh so, let's recap from the beginning uh with the challenges. So, we have the largest level uh of the game, that this
is an open world, basically. We need new faces for our characters uh because we want to have this miniature / diorama feeling and to enhance the uh travel feeling of traveling. And also, we have of course Enki, that is uh quite new uh because he can swim and fly, and this is nothing this is something that we did not have before that. Of course, these challenges comes with uh solutions. So, if we look at the largest level ports, uh this was one of the last levels uh that was entering into production. So, at some at this moment, we kind of knew what we were doing. Uh I think we we thought so. Um and it means also that because it's one of the last level, we can use absolutely everything that we have for the regular levels. So, for example, we as Tom explained to you with the enemies and battle systems, uh we just reused everything. Uh we didn't change anything to the battle system, and we just use it on the world map. Uh same thing for everything related to interaction, looting, NPCs, uh dialogues, everything is the same. Uh same thing for the level scripting
and the game flow. Uh we are using the same tools. And also for the level portals and transition, uh before that, we were just traveling between one level to another, and we just added this new hub uh that is the world map. In terms of uh environmental art, sorry, uh we decided to uh gave freedom to our artists to create the world map. The goal here was to avoid constraints to faster the iteration because this is a huge level. So, we wanted our artists to work fast uh on this level. And also, we did not create a lot of new props and assets for this level. We reused a lot uh and in vast majority, the level art that were coming from the over levels. Also, uh to help us build that, of course, we rely again on building systems uh Open World. Uh first thing is the world partition that allows us to uh split the world in multiple cells that are streamed um uh beautifully for us. Uh all artists can work together on the world map by using the OFPA one file per actor. And also, we use uh HLODs. I will come to back I will come to that, sorry, a
bit later. Uh so, let's talk about the gameplay now. Um with the faces. So, this is the character in uh a regular level in Expedition 33, and this is the character in the world map. And the beautiful thing is that that's the same object. This is the exact same class. Uh we didn't change anything. What we did is we tweaked a bit our camera and physics parameters, but we did not develop any new physics development for the character on the world map. What we did is adding some kill switch for the features we didn't want. So, for example, the character cannot jump or cannot climb on the world map. So, we just did some kill switch uh to the systems. Also, we um added some blur in the background, some tweak to the post process on the camera for this Diorama uh aesthetics that we wanted to have. And uh that way you have kind of a new look and feel at a at probably no cost. And then of navigation, uh characters can uh navigate only in dedicated zones. Um here we are using the exact same thing that we have in other levels. So, all uh
artists and designers, level designers, have a tools to have a tool, sorry, to set up invisible walls easily. Basically, they are drawing a spline that can be looped or not uh in the environment, and automatically uh invisible walls are extracted from this spline and uh baked uh at the end for better performance at one time. Let's talk about SK now. So, SK is, let's say, obviously unique, but also derived completely from the main character. Again, we did not create a new class for SK. You can see it there. This is a screenshot uh from Unreal, and you can see that with the world map character, that is SK, uh directly inherits the class that is the character world, that is the normal character. Uh we used, of course, our kill switch that we just coded before uh to remove all the features of the main character uh to SK that we did not that we didn't want. And this way we have SK roaming on the world map. Oh, yeah. I forgot to mention, but obviously we switched the mesh and the animation graph. So, you have kind of something new.
Let's talk about flying before swimming. Uh so, we added the flying state on the character. Um and the cheat here is that this is building in Unreal. Uh we did not create this new state. basically in the default character movement in the real engine you have a flying state that is disabled by default or only used for the fly mode for developers only. But here we just yeah, give it to the character and put some inputs to it and we have a fly mode after a few physics tweak. We have one issue though that is quite hard to solve and that is where we can land and take off because we don't want Eskay to be able to land everywhere and we don't want him to be stuck in any collision. So getting into the landing part. We have a few ideas here, a few ideas here. The first one would be to use the physical material properties on the landscapes but this was quite incompatible with the artist freedoms that we had for the visuals and it was also too imprecise so we dropped that idea. The second idea was to paint the zones on textures for the landscape but again
this was a bit too long to do and this one needed to be redone each time the level changes so we did not want also drop that idea. And what we did is using a navigation mesh. We created a dedicated nav mesh for Eskay but this nav mesh is not used for navigation. In reality this is used only to know if Eskay can land or take off to something. This is not perfectly accurate because nav mesh is a simplification of the collisions and the and the meshes but that's quite good enough for overall in the end. To manage this nav mesh so we will use a custom trigger shape that we have in overall levels to manage well triggers in the game. This was useful to create exclusion zone and also no landing zone in some edge cases. For example in floating islands that was a bit complicated to manage so we have some custom trigger shape to manage exclusion and no landing. And this is what you can see in terms of nav mesh. So this is the main nav mesh of the games. This is used by the companions
and the enemies for roaming. And here this is nav mesh of Eskay and you can see that because Eskay is much bigger than the character and we don't want him to be able to land close to collision, you have some big borders around each edges of the invisible walls. And here is a demo with debugging enabled. So, this is a nav mesh and basically when SK is flying, we raycast the ground. If we detect a nav mesh, circle is green, meaning that SK can land. If the circle is red and we don't detect a nav mesh, SK cannot land. As simple as that. Uh pros and cons again. The biggest advantage here is that everything is automatically generated with a minimal setup. Uh all artists just just need to set up mesh volumes mesh volumes and some exclusion zone and automatically everything is generated when we bake the game, meaning that we don't have a lot to tackle to say, "Okay, this this part is landable, this part is not landable." Uh and also, as I said, this is good
enough for most of the time. I don't think uh anyone has issue with our approach. Uh again, some edge cases that needed to be done manually, but that was quite manageable. And also, the biggest con here is that you end up with two navigation meshes in memory. That is quite heavy, especially on modest hardware. Um but in the end, because we are using the world partition and we are using the partition nav mesh, everything is also streamed with the different cells of the world. So, we worked it out. Let's talk now about swimming. That is probably the most cheated feature cheated feature of the game because swimming is actually walking on a giant plane. Uh you do not swim in Expedition 33. We just put below the water a gigantic plane and uh actually, SK is just changing animation and physics and that's pretty much all. You continue to walk. You're still in the walk state internal in the walking state internally. All right, we need to do is to detect the water to be able to switch between these two
let's say states. Uh so, the walk cable, the walking, and the swimming. And we encounter a new problem that is how we how we how can we detect the water here? Uh so the first approach well and our approach was to only detect the borders. So we are using here our invisible wall invisible wall tools that I just talked before. So our artists and designers draw a spline and this spline represents the border between land and water. So when when SKA approach this trigger then entering it we just switch animation we just switch animation and physics and we ready to go. Uh this triggers also can also blocked characters and if the feet if the swimming feature is not unlocked SKA is not unlocked SKA is also blocked by this triggers. Uh we still need to have water detection for the landing path though. Um because when we are landing we need to know if we need to go to the swimming state or the walking state. At the
beginning uh we were using our custom trigger shape for the water zones but in the world map we have way too much water and it takes way too much time to set up. So we decided to reverse that and saying okay by default SKA is swimming. And we will only use the custom trigger shape to say okay this is walkable. Um and in the end when you need to land well on top of the way cast with the navigation mesh we also way cast these trigger zones these sorry yeah custom trigger shapes and if we detect the if we detect nothing it means that you you will go in swimming state if you detect something you'll go in walk walking state. Um a bit about the performance and the view distance here. So on the world map of course players accept long view distance in an open level because this is an open level even on most on modest hardware of of course. And here we are using the SHDs that are comes from free uh Unreal that allows us to draw far away actors. This is set up directly by our technical
artist and our engine programmer. Uh this requires a lot of memory. Uh so, we decided to um permit tweak the parameters in uh another level in a smaller one so we can uh fasten the iteration uh accelerate the Yeah. Have a quicker iteration. Um and in the end uh when we are satisfied with uh these parameters, we just uh uh well, we replicate it into the world map and we build and we build and bake the world map. And in the end, this is the only level to use uh HLOD in the game. We don't use it in in any other level. Uh let's conclude. So, we decided to give freedom to our artist to build a unique level so they are free to do whatever they want. Uh we will utilize a lot of our other systems with only slight modifications or at least uh we try to keep it as simple as possible. Uh we have a few tricks on the freezes uh that come up with a kind of a new look and feel. Most of the Most of the development time were spent on was spent on SQ because we
also built a world map to showcase SQ. And we rely on Unreal as much as we can. So, I already talked about the nav meshes, the HLODs, and so on. Uh the OFPA, the world partition. On our side, uh as I said, we will use a lot of system and we mostly develop only stuff for SQ in term of data tools and scripting API. Again, we are using a lot of tools, namely custom trigger shape and invisible walls, and we only extend that for the world map zones. And uh for our designer and artist, we will use massively the level art from other levels to create a new level design. And I think we are quite behind the schedule, so please don't wrap it up.
[laughter]
Okay. Um so, yeah, just to to wrap this up. Uh the core idea that we kept throughout production was to as programmers to keep the creative uh freedom of the designers uh in mind whenever we were designing a new system for it to be easy to use. We ourselves we use as much as we can. It's true for visual assets, but it's also true for
systems. We can we try to push everything to to their limits. Yeah, you have to have that mindset of empowering your designers to create content. It goes through both providing them, you know, nice building blocks, but also having safeguards because in the end safeguards are a vector of autonomy because yeah, they can create some new content from A to Z without programmers having to be involved unless there is really like a very complex issue to solve. And I think the most important takeaway is just yeah, trust your team, trust the designers because most of the time they will use your systems in much more creative ways that you would have imagined as a programmer. And I think that's also the beauty of game development with, you know, all the teams coming together to to build something something interesting. So, yeah. Thank you.
[applause]
We will take a few questions. So, there are microphones on yeah, the two aisles.
Thank you. Oh, I think I think we're done.
Oh, maybe we are not.
Oh, we don't have the time.
Okay.
[laughter]
Okay.
Well, you can catch us yeah, at the after the room. Yeah. Thanks.
Hi. Hello. So, um My question was basically um I just wondered like how much uh time did it take to upgrade Unreal for you? And I'm assuming you started on Unreal 5, right? Because I have I'm working on an older code base that was Unreal 4. And one of the problems that I could have when I upgrade is that often my features get deprecated that I was using before, and that causes a lot of pain for me sometimes, and I didn't make
those decisions. But I was just curious, like since you started probably from 5, I'm assuming, like how long does it take for you guys to do an Unreal upgrade? And how many programmers do you need?
So yeah, it's a very hard to hear the question, but I guess
So my question was, like how long does it take for you to do an upgrade? Like and how many engineers does did it take?
Yeah, okay. So the transition from Unreal 4 to Unreal 5, it took like a few months because there was, you know, a preview version of that, yeah, new version of the engine. So we would basically have a parallel branch for the programmers to, you know, try to make that work, try to generate, you know, a package build, and all that. So at the same time the team could, you know, keep working on the actual game and and the content. So yeah, that particular version took a few months. But then after that it was, yeah, the biggest version update that we we had to do. After that it would take between 1 week to
three or four when it would, you know, when there would be some breaking changes in the APIs, all that kind of things. So yeah, it it can get quite long because, yeah, for us three or four weeks of programmer time is very precious because we can do a lot of you know, development for the rest of the team in that in that time lapse. So So yeah, that's the the range for us.
Thank you.
Thanks.
Yeah, I'm an indie developer. You are an inspiration to indie developers everywhere, by the way. Yeah. And so one thing I you you're saying that at the very end some programmers from Kepler for example swept in to save the day. What what can we expect you know as a as an indie developer who will hopefully get a publisher one day? What can I expect um
you know these a publisher to clean things up at the end or do I need to basically get get everything perfect before that stage?
Yeah. I I I think it really depends on the publisher that you're working with. In our case Kepler they do have like a a technical team. So yeah, they had the opportunity to help us. But I would I would say if that's not the case with you know your your publisher usually that's the situation where you would you know have an external partner work with you on you know specific platforms. For example for us the Xbox Series S was like quite hard to optimize for. So yeah, that's when yeah having external people coming to help is really useful. And in our case yeah it was a mix of external people and people from our publisher. But I think it can really be any kind of structure. So yeah.
Thank you very much.
Yeah.
Hi. So my question is how did you go about marketing yourself before you received funding and publishers?
Okay. Go ahead. It's good.
about marketing yourself to publishers and getting publishers before that happened.
So yeah, our approach to that was once we had the vertical slice prepared we actually came to GDC so in 2022 and we had as many meetings as we humanly could to, uh, yeah, to show the demo, uh, that we had. Uh, we were also on the show floor, uh, at the time, um, to, yeah, with the demo so people could just, you know, drop by and see what, uh, what we have, what what we had built. Uh, so, yeah, that was, I think, the time we had the most, you know, the
most meeting and the most opportunities to showcase our work, but we also had other times, of course, back to back in France, uh, with more, uh, local, uh, publishers. But, yeah, I, yeah, it was only as many meetings as we, as we could in, uh, yeah, show floor, hotel lobbies, uh, hotel suites, and that kind of things.
Thank you so much.
Thanks.
Hello. Uh, I was wondering, um, was there anything that you had to, uh, cut because of the programmer side, uh, programming team size? And, uh, what was your sort of philosophy for determining what to keep and what to cut?
Uh, yeah, uh, I think one of the, uh, strong suit of Sans Souci is that like, uh, we have a very strong, you know, creative direction. Uh, Guillaume in particular, he really knows, you know, what he wants. Uh, so in the end we ended up, uh, cutting very little from what the programmers, uh, had built. Um, so, yeah, not all
productions, uh, are like that. We are quite lucky that, uh, yeah, Guillaume had, you know, his dream game, um, in mind. Um, so, yeah, I think it's also an important component, uh, when you are a small team. I didn't really mention it, but, yeah, staying focused on what you will actually ship, uh, to the players because, yeah, any thing that you take time developing that does not make it to the final game, it's, uh, yeah, a lost, lost time for improving other features that will actually, yeah, reach the players. So, yeah, I think it's uh, it's up to the designers, you know, to yeah, to have a clear vision in mind, but it's not always possible. So, that's why I'm saying we are lucky, you know, to to have that. So, yeah.
Thank you. Hello.
Yeah.
Um do you have like one main best practice or best tip for how to like properly architect a game with like
architecting your game systems like before you start on them rather than like just going with the flow
[laughter]
or what I you know that is you know
Yeah, I mean to be completely honest we kind of went with the flow on Expedition 33 because like yeah, the project grew super organically. It's literally the same, you know, project files from 2019 up to what we actually shipped. So, we do have, you know, a lot of technical technical debt when it comes to, you know, the battle buffs system for example. We kind of improved the UX for the designers along the ways with, you know, easier nodes that came in later in production. So, yeah, we have you can tell when you look at in depth at the project that there are, you know, various stage of refinement of the building building blocks that we that we provide. So, yeah, now we are starting to work, you know, on the next project. So, we have the time
to okay, rethink about all that and see how we can improve that mindset. Still give freedom to the designers, but yeah, having a more consistent structure. I think it also helps a lot to have, you know, a single way of doing things in the project for designers because as soon as you have several ways of doing the same thing, it's starting to yeah.
Yeah, no, that makes sense. Thank you so much and thanks for making a great game.
Thank you so much.
We good?
Okay, and that's all the time we have. Thank you everybody.
[applause]
Auto-generated captions from YouTube — names and numbers may be misheard.
Game Developers Conference · 2026
Game Developers Conference · 2026
Jason Schreier · 2026
Comments