ARRAN LANGMEAD: I'm Arran Langmead,
technical artist and evangelist for Epic Games. And this is a guide to
open world production in Unreal Engine 5. Open world creation is a
major part of development in the games industry. Everything from
Assassin's Creed to Zelda features vast,
sprawling worlds for players to explore and open boxes. Despite its popularity,
open world creation has continued to be a difficult
task for many developers. But with the release
of Unreal Engine 5, creating these open worlds
has never been easier. In this talk,
I'm going to give a complete overview of the new open world
features and how to set them up. This includes
One File Per Actor, World Partition,
Hierarchical Levels of Detail, or HLOD for short, maps,
Level Instances, and Data Layers. Before we dive into
these new features, though, let's just start
with a little bit history to explain why these
tools have been built. Now, generally when
you're working in Unreal on an open-world project,
you have two main problems. The first is working
collaboratively, and the second
is working at scale. Now,
I have two Unreal Engine 4 projects here to demonstrate these issues,
the Kite Demo and Shooter Game. Starting with Shooter Game,
we have a relatively small map. Now, even working at
this quite small scale, we still have issues
working collaboratively on it. And that's because of the
level structure that Unreal uses. All of this data, all of these
positions, the actors that are being used,
the audio, all of that stuff is being held inside
the level file, in this instance called High Rise. And this is a binary file. What that effectively means is
that only one person can right click, check out that file,
and work on it in Version Control. Once that's done,
nobody else can touch it. Now,
there have been workarounds for this, and that comes in
the form of sublevels. So you create a number
of sublevels that are always loaded in, and they represent
different sections of the game. Often, they'll be
separated by the disciplines that the people are working in. And this has all been
done so that people can work on these
individual elements separately while working on
the level as a collaborative whole. But this isn't an
ideal solution, and it gets even worse at scale. So looking at the Kite Demo,
we have this thing called World Composition,
which is basically just a manager that
handles all of the streaming for your sublevels. So I have my Levels tab
on the left here this time with all of the different levels
that I have inside this map. And this is actually
quite a small number of levels considering
the size of this map. You can get incredibly
granular with it. And what this allows
me to do is import a grid of levels,
and then from that grid and from those levels,
it will generate all of the streaming distances that I'll use. I can create different rules
for those streaming distances, and then I can also
use it to generate logs as well, which is in this
bottom left panel here. But this wasn't an
ideal solution either. World Composition still
uses the same level structure, which means that
even if I'm only working in this small section
of this 4-kilometer landscape, I still have to check out
the entire section in order to work on it. And because we're still
using the same level structure, I have to go in and
manually make sure that I'm marking this as
current if I want to work on it. Because if I start placing
actors down with this cell marked as current,
even if I'm placing them over here in this
top left corner, they'll still be
marked as loading from this particular cell. So really, the key difficulty
with open-world development has always been down
to the level structure that the engine used. And fixing that required
a much larger rethink into how level data is
stored and separated. Let's look at how
these issues have been addressed
in Unreal Engine 5, starting with One
File Per Actor. Now, here's a level that
I've done for this video, and it's currently done to a
placeholder plus standard. So I've got my assets,
which are really just representations of
the volume and the color that they would be. But they're no more than that. They're just put there
for me to be able to place. And then I've also
just gone through, and I've scattered as
much of this stuff as possible around the scene. So you can see we've got
a reasonable approximation of what the level of
density on these assets would be even if it's
not the final clean-up on those particular assets. You might also notice as
well that I have this little guy dotted around the
scene who might be in a bit of a
predicament in a few places. And I'm using this one to get
a scale reference for everything I'm doing. So this is actually just a
debug that I put in every scene that I'm working in just to
make sure that I'm always working at scale. And I find that really,
really useful when I'm working at
a scene of this size. So as I said, this is my level. And on the surface,
this looks just like a normal level,
something that you might even see inside U4. But there's a few key
differences with this, and the main one
is One File Per Actor. So I have my level
here like normal. This one's called Moor,
and that's the particular level I'm working in. But you'll notice on the disk size,
it's about 8 kilobytes. And that is
significantly smaller than some of the other maps
that we were working with. If we look at the
Kite Demo assets, those maps can be anywhere
from several thousand to 300,000 kilobytes in size. So the main question is,
what's happened to all that data that was sitting inside our level? And the answer to that is
the External Actors folder. If we have a look
at our Contents folder inside the browser,
you can see we have the Moor map here,
which is 8 KB. And then if we go to this folder,
called External Actors, we have a folder for
every map that we're using One File Per Actor in. If I go to Moor,
you can see that we have a huge number of folders. And inside those folders,
we may even have more folders with files. And each of these
files is actually a reference to an
asset that's in this world. So every single asset,
or almost every single asset, is actually an actor held
inside the External Actors folder. And that opens up some really
interesting workflow options because now we have
these individual file references. So let's say this actor over
here is now a separate file. That means I can move it,
I can change it, I can scale it, rotate it. And I'm not
interfering with anyone else who may be
working in this single level. So now we've gone from quite
a large-scale workflow where we had to check out
an entire level to a really granular workflow where
we check out individual actors. And we can actually preview
this inside our World Outliner. If we go to the right here,
we can enable Source Control in our columns. And then when I move this,
it will actually check this out. And we get the little warning
come up, check out actor. And you can see that this is
the external actor reference. So we're looking at Moor,
3DY9, and then we get a long string
which represents the actual actor itself. And we can press Check Out. And now we have
a little green tick next to this particular asset. And this is now
checked out by me. And anyone else who
wants to work on this map can still do so. They can work on any of
these actors in the world, but they can't work on
my checked-out actor. Just one last tip with this
to make things a bit easier. If you go into your
Editor Preferences under Edit,
and then under Loading and Saving,
go to the Source Control setting, and then tick
Automatically Check Out on Asset Modification,
this will automatically check out the file for
you when you move it. If you want to use
One File Per Actor, that's a really
easy setting to do. So we just go to
our World Settings, type in External Actors,
and you'll see the tick box here. This will be on for any
world partitioned map, and it just means that you'll
be able to work much more collaboratively for
all your projects. Now let's talk about
World Partition. You can think of World
Partition as the fully realized version of World Composition,
allowing you to easily break up your world into those small,
streamable chunks. If we look at the
World Partition section, it actually looks really
similar to World Composition, but there are a
few key differences that make it a much
easier tool to work with. Before,
selecting and loading and unloading cells inside World
Composition was very slow. But with World Partition,
this just isn't a problem. We can select as many cells
as we like without any slowdown whatsoever. And loading and unloading
is incredibly quick as well. Here, I unload the entire
level like so and then right click, load back in again. And a few seconds later,
we've got the entire level loaded back in. But the most useful
part of World Partition is that the grid used to
split up this world into all of those small chunks is
now separated from the level and from the actors in it. And that means the
actors are automatically just assigned
to the correct cell that they're supposed to be in. That means that I can
take an actor like this, I can duplicate it
over to a new cell like this one over the river. I can press Save. And then when I select
my old cell and unload it, you can see that
mesh is still there. And if I unload the new
cell where my mesh is, you can see that it disappears. The way we control how
these meshes are assigned is in the Details
tab of the actor. Under World Partition,
we have a dropdown called Grid Placement where
we can choose whether the actor is streamed in based on the bounds,
location, or whether it's just
marked as always loaded. But what that also means is
that we can change our grid sizing for how this world is broken
up at any time in the editor. Under World Settings
and World Partition, we have the runtime
hash and its settings. We can choose to have
multiple streaming rules. In this case,
I just have the one. And we can choose the cell size,
loading range, and we can even
preview this as well. So you can see
my cell size here is set to 25,600 with my
loading range set to 51,200. I can change this just
by typing in any number. Let's try 10,000 to
get a smaller grid. And once I save,
all of the meshes inside here will automatically get
assigned to their new grid cell. This is incredibly useful
when working on a project because you don't necessarily
know how big your cell sizes need to be until you've
populated the world that you're building. You might also notice
that the cell size listed here is actually different
from the grid size listed under World Partition. And that's because the grid is
locked to work at 512 meters. So all the cells that
fit within this square will get loaded in
when we need them. If you want to enable World
Partition on your project, that's also quite an
easy thing to do as well. If you get to Edit,
Project Settings, and go down to World Partition,
we can enable World Partition,
which will make all new
maps automatically enable World Partition. We can also tell the Editor
to enable a conversion prompt,
which will ask if you want to convert your current map
over to a World Partition map whenever you load it. If you already have
an environment inside World Composition,
we can convert that as well. We have a custom
commandlet that you can use. Just convert your
project to Unreal Engine 5 and then run the commandlet
targeting the master level. World Partition also comes
with a mini-map feature as well. Your map may not look
like this when you first load it. But if you go to the
Console Command section, type in WP mini-map
show reload button 1, that will give you a button
inside the World Partition map letting you reload
the mini-map It will capture an orthogonal projection
of your environment, and it will create an actor
inside your world outliner called World Partition
Minimap that gives you reference to that as a texture. You can also set the map
size that you want to use for it. World Partition can also
handle all of your distance logs as well. If I press Play on this scene,
nothing really looks like it's changed. But actually,
the vast majority of these assets have actually been swapped
out for their proxy art equivalent. If I go to the Command
tab and type in wp.runtime and then HLOD and set that to 0,
that will disable all of the
HLODs that I currently have in my environment. And you can see that
the vast majority of assets have actually been removed. If I turn this back on,
you can see that all of our assets return. And we can preview
which cells are currently being loaded using
WP runtime toggle draw runtime hash 2D or 3D. The center of the circle
represents the player character. The outer ring
represents the distance that we're generating around. The line represents
my view direction, and the green squares
represent the cells that are being loaded in,
red squares being the cells that are loaded out. The World Partition
generation is actually set up in a commandlet,
but we have a few settings that we can modify in our Editor first. Inside our World Settings,
we go down to our default HLOD layer, and we can create a
HLOD asset from this to specify what the default
HLOD generation is going to be. For this,
I've set it to instancing, which groups all
of the same meshes together into a single lowest
logged instance and marked that as always loaded. But I can also override this
setting on any other mesh that I want. For these background
assets and for the rock assets, I want to use a different
HLOD generation. And for that one,
I can just click on the asset, go into the Details tab,
down to HLOD Layer, and you can see I've got
HLOD rocks set here, which is another HLOD layer
type which I've created. And on this one,
I've chosen Simplified Mesh. So Simplified Mesh will
group as many of the meshes together into their
own logical clusters, and it will generate a new
simplified version of that mesh that has a single material. I can set all of my settings
for this in the Proxy Settings. And you can play around with
these to get different results. We can also mark these to
generate Nanite-enabled meshes so all of our background
assets are using Nanite. Once we've assigned
all of the actors we want to their
correct HLOD layer, we can run a commandlet
to convert these actors over. This is done
outside of the Editor. And I've created an example
commandlet to show you here. The way that I
generally do these is by creating an Unreal
Editor shortcut or BAT file with the
required arguments. Here's the general structure
of the executable or BAT file. You navigate to the
path of the Editor. You then navigate to
the path of the project that you want it to
perform the operation on, then the target map
inside that project, and then you run the
task that you want to do. So underneath that,
I've just got an example list of where your directory might be,
where your project might be, the map file itself,
and then the operations that
you need to perform. So with this one, I'm generating
the HLOD layer inside here. And that way,
all I need to do whenever I want to regenerate my
HLODs on this particular map, I can just double click and
open this particular executable. Generating HLODs this
way rather than having a button press inside
the Editor means that you can create a
dedicated machine to generate your HLODs for you. This is incredibly useful,
especially as the HLOD
generation process can take a very long time depending
on how big your scene is. So I've explained how,
in this new system, we go from having
a multiple-level file structure to a
single-level file structure. But that creates an
interesting problem, and that's one of
separating out content. One of the really useful
things with having sublevels was that you could control
their streaming in and out with Blueprint. So how do you go about
doing that in the new system? And that's where two new
systems come into play-- Data Layers and Level Instances. Let's start with Data Layers. Inside the Windows tab,
you can find the Data Layers tab. I already have mine
active over here. And what Data
Layers allow you to do is have a very
granular level of control over what assets you're
streaming in and why. So let's say we want to create
a mission where our player is tasked with catching some fish. And I have some
markers in order to do that, so I have this mission
marker over here. And I can place a
few of these down to guide the player where I
want them to go, in this case to the river. So I've created these
mission markers, but I don't always
want them to be loaded. I only want these
missions to be-- I only want these
markers to be loaded when the player starts the mission. So the way that I manage
that is by putting them into a Data Layer. Now, at the moment,
we don't have any Data Layers, so we need to make one. So I'm going to create
an empty Data Layer, and I'm going to rename
this mission_getfish. And then all of
these markers I'm going to assign to the
mission_getfish layer. So I can take these layers,
I can hide them and unhide them using this
little eye symbol, and I can control how
they're streamed as well. So if I turn on Is
Dynamically Loaded, I can set the streaming
rules for these items. So the initial
state for these is I don't want them to be
loaded because I only want to load this in when
I start the new mission. I don't need them to
be as initially visible. And for this one,
I don't need to generate HLODs because I'm only going to
have this mission data in here. So I'm not putting in
a lot of mesh content, so I don't need to generate
HLODs for these either. And now when I press Play,
you can see that these
markers aren't visible. And that's because
they're not loaded in. We're not using them at the moment,
and they're unloaded. But now we need to
load these assets in. So for this example,
I'm just going to expose these to a key press. So this is my base
character controller, and I'm just going to add
the action event start mission. And I'm going to get
Data Layer subsystem. Set the Data Layer
state to activated. And I'm just going to
promote this to a variable and call this active mission. And we're just going to
set that to mission_getfish. And now when I
go in and I press 1, my markers activate
because my Data Layer is now activated and loaded in. And that means that I
can follow these markers all the way to my target location. We can also preload
the content as well. So at the moment,
I set it straight to activated,
which means that the content is loaded and activated
as soon as it's ready. But if we know
that the player is going to activate the
content at some point and we want to have
it loaded in advance, we can do that very simply. I'll just create a quick custom
event called pre-load content. I'm going to just copy all
of this stuff and put that in. And rather than
set it to activated, I'm just going to
set it to loaded. Now when this event fires,
we load the content, but we don't display it. And then once we're
ready to start the mission, we can set the
content to activated. What's also great about the
Data Layer assignment is that you'll notice on the Data
Layers section for the actor, it's actually an array,
not just a marker. That means that we can assign
actors to multiple Data Layers so that they could be
used across different sets. Let's say once we get
to the end of this mission and we have our fish, we want
to put a fish generator in there so that our fish can
spawn in this region. And we want to put that inside
our current get fish mission. But let's say for
some unknown reason that you want to have more
than one fishing mission. We can make a new Data Layer,
call this mission_getfishadvanced. And then we can set up
some new markers as well, say through this
little section here. But we want to end
up in the same spot. So we can select these markers,
and we can put those into the Advanced Fishing tab. But then we can also put our
fish spawner into that as well. And now we have
two Data Layers which share the same fish spawner. So if we hide this one,
you can see that our fish-- you can see that our
fish spawner stays. But if we get rid of the other one,
then they both disappear. If we bring advanced in,
then we get our fish spawner back. And this allows you to create
really complex streaming rules with a very simple
array structure. A great example of this
at a much larger scale can be found in the Valley
of the Ancients demo. In this example, we unload
massive sections of the world and replace them with
completely new ones. Once the interaction happens,
we create a sequence to hide the transition. The old geometry is unloaded. Keep your eye on the top right
as the transition is happening, and you'll notice that
the Campfire Replace level becomes unloaded and
the Dark World becomes activated. Once we fade out,
we have a brand-new world for the player to explore. Let's look at this again,
but with some of the transition effects removed. So you can see we currently have
Campfire Geometry and Campfire Replace loaded into the
world with Dark World unloaded. As we walk over,
we'll trigger the transition effect. We can then press E,
which will trigger the transition. I've added some break points
here to show what's happening. Inside the game mode,
we have our Data Layer set. And we're setting Campfire
Replace to unload it. That's because we don't want
any of these actors in the world. Then we tell it to
flush that content. The content's been unloaded,
so now it tells the Dark World to activate. Dark World is activated,
and we now have our new scene. I've disabled some
of the other features as well, like the color
correction on the assets, so you can see a bit more
clearly what the Dark World is adding to the world. So you can see
that Data Layers can be used in a vast
number of ways, from very small changes
like creating missions to massive changes
like completely modifying an entire world. Finally,
let's talk about Level Instances and Packed Level Instances. And again,
there's no better example of this than in the Valley
of the Ancients demo. If you open up the
Ancient World map, you'll find that the vast
majority of the assets placed in the world are made
using Packed Level Instances. Put simply,
these are prefabs that you can use to group content
together to place in the world. These have a reference
to the Blueprint asset itself but also the Level Actor. If we open this up,
we can see the prefab inside its own
independent level. The prefab will use this
level as the reference point for its actors,
which means that if we place anything inside this-- like, let's say,
this cube, and save it-- when we go back
into our Ancient World, find our actor in the scene,
right click, and tell it to update
the packed Blueprint, the Level Instance will
be updated in the scene. You can also break this
content into the world as well. If we right click on this,
go down to the Level Instance section and press
Break Level Instance, say yes to the warning,
and this will delete the pack level instance in the
world and replace it with regular static meshes. As well as Packed Level Instances,
we can also add Level Instances directly to the world. Inside the Create tab,
I'm going to open up the Place Actors Panel, type in Level Instance. And we can drag and drop a
Level Instance into the world. We can then select any level,
including the packed references. And we can also select
what the runtime behavior is, whether that's embedded where
we discard all the Level Instance data or whether that's
set to level streaming, and it's streamed
in like a regular level. So to sum up,
we have a new system in place that replaces World Composition. That system is
called World Partition. And the great
thing about this is that you can convert any
level to the new system. As well as this,
World Composition will still exist in UE5,
so you will be able to convert your
projects over and keep the pre-existing level if World
Partition doesn't work out for you for whatever reason. One File Per Actor
lets you check out individual actors in a level,
and those actors have now been moved
to a separate folder. So you will see a much
smaller level file size, but that's split over a
much larger number of files. The great thing
about this is that we can work in a single level
without having any checkout issues,
allowing multiple people to work at a very granular level
on a huge level and scale. We have Data Layers
and Level Instances which will allow you to manage
groups of content, separating them out,
and allow you to control how those elements are
streamed in and out of your level. Please keep in mind
these features are part of a preview release a UE5,
and as such, are experimental. We do not recommend
shipping your project with UE5 in its current state. This is a chance for you
to try out these new tools, play around with them, and give
us feedback on how they work. And that's everything. I hope you found this guide
to new open world tools in UE5 useful. If you want to find out more,
you can check out our Docs
page on all of the new features and download the Valley
of the Ancients demo from the launcher
for a live example. If you have any questions,
reach out to me on Twitter at @arranlangmead.