Large worlds in UE5: A whole new (open) world | Unreal Engine

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
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.
Info
Channel: Unreal Engine
Views: 77,527
Rating: 4.9605751 out of 5
Keywords: Unreal Engine, Epic Games, UE4, Unreal, Game Engine, Game Dev, Game Development
Id: ZxJ5DG8Ytog
Channel Id: undefined
Length: 29min 32sec (1772 seconds)
Published: Tue Sep 28 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.