VICTOR: I'm your host, Victor Brodin. And my guest is Ben Zeigler-- Unreal Engine consultant. Awesome. Today we're going to talk a
little bit about the Asset Manager. Ben, the floor is yours. Please go ahead. BEN: Sure, yeah. I just want to talk
through the Asset Manager. This is something that's
been requested a few times. It's pretty programmer
focused, but also designers. And anyone that's
interested, it's kind of more depths of the engine will
be interested. Just a few slides I
want to start out with. Just talk through
the basics of it. Yeah, so the Asset Manager. Before we get too
far into that, just want to do a quick
intro for myself. Hi, I'm Ben Zeigler. There's my email address
and my Twitter handle. I originally developed
the Asset Manager when I was a full time Epic
engineer a few years ago. Now I'm working as an
independent contractor exclusively with Unreal Engine. And I also help out by
contributing directly to the engine sometimes. My experience is kind of
half gameplay and then kind of half in the more core
parts of the engine. And then the relevant part
for this discussion is, I worked on both
Epic internal games, on the engine gameplay
framework-- on Blueprint team-- and I also worked
on the action RPG sample, which I was on here a
few years ago to talk about. So what is the Asset Manager? So it's a single global object. It's very much like an
Engine Subsystem, which is kind of a newer concept. But it's a bit older
than an Engine Subsystem. But just treat it like
an Engine Subsystem. It's not map or mode specific. There's only one of them
even if you're in the editor or in the game. There's only one. One of the main things it does
is it categorizes and queries unloaded assets using
something called the Asset Registry, which
I'll get into in a second. It also maintains a global
asset loading state, which means it keeps track
of which things are in memory and which things
are not in memory. And it also integrates a
bunch of existing systems that were spread out. So it talks to the
cooking system. It talks to the
async loading system. And Asset Registry,
like I mentioned. It was also designed to
be overridden and extended by individual games. So it's kind of designed
with a little bit of game specific extensibility in mind. So I mentioned the
Asset Registry. So the Asset Registry is
used for a bunch of things not just the Asset Manager. The main thing it's used for
is powering the Content Browser in the editor, which keeps
track of all of your meshes, all of your textures,
all of everything. It does this by
storing information about assets that are saved
even if they're not in memory. So it can know about all the
meshes in the entire game. When you load your
editor up, it actually refreshes that off
of like a disk cache, which if you've ever loaded the
Content Browser, you see a progress
bar in the corner. That is the Asset Registry
loading up all the information off of the disk. So the other thing it has,
is it has a key and value map for every single asset
that allows you to store kind of arbitrary data in
the Asset Registry at save time, which you can
then read back later on. And the Asset Manager takes
advantage of that capability. Most of the data is stored
in the Asset Registry is also available in a
cooked or packaged game, but not all of it is. We will get into that
a little bit later. And it also provides the data
storage for the Asset Manager's internal systems. OK, so the Asset Manager-- why does it exist? This is actually a kind of
a hard question to answer. We were talking about how to
describe the Asset Manager and what the purpose of it is. It's actually a little
bit complicated. Because when you're just making
a prototype game or a very map based game where you go from
the first map to the second map and it's very static, you don't
really need an Asset Manager. You need an Asset
Manager when you're going from that static prototype
into a more complicated game that has a lot of
different things that interact with
each other or you need to know about
from other places. It's especially
important if you're making like a role
playing game or something like a roguelike,
where there's a lot of different types of things. So this came about
originally because we were making a game like that
with lots of things in it. We started with something
called the Object Library, which is used in a few places still. But it's kind of like an
older version of the Asset Manager that just keeps track of
a very specific list of objects and let's you read information
about them from the Asset Registry. But that was limited because
we wanted to keep track a lot of different types of things
like weapons and potions and all sorts of
things like that. So the other problem with that
is the Blueprint classes work a little differently
than other assets. And they're a pain to
work with sometimes, so the Asset Manager
hides that complexity. It also helps deal with long
load times and high memory usage. Because the other
way you can avoid having an Asset Manager is
if you just load everything into memory at the
start-up of your game. Then you don't really need one. But also you're
wasting a lot of memory and then your load times
would be like minutes long at the beginning of your game. And no one wants that. The other thing
is we were trying to move from a lot of cases
using hard you UObject references and loading
those when you need them. But that is a problem
because that can often cause really bad stalls in your game. Like you might just go
try to do something, and you need to wait five
seconds for it to react. And players hate that. No player wants that. So we were trying to
move away from that, and the Asset Manager was
a tool we used to do that. Another thing we
wanted to support is, when you're going from
your main menu into your game or you're on a
client on a server, you need to load
different types of data. And that is kind
of hard to track, so we wanted to have
a system that can handle tracking that for us. Then the last
important thing it does is it ties into the packaging
and chunking system, which decides which files things end
up in on your final platform-- on your console, or your
mobile device, or on PC. So that was also a
complicated manual system that the Asset Manager is
designed to interact with. So there's a few a big
components to talk through. I guess the first thing is,
what is a Primary Asset? So a Primary Asset is
anything you would talk about as being a top level thing. So a map, like a level,
is a Primary Asset because you can talk about
it anywhere in the game. But you also might have other
Primary Assets like weapons or potions or up to
50 different types of Primary Assets depending
on what your game needs. So there's one type for
basically every single class. So there's a potion type,
a weapon type, a map type. Then there's also
an identifier which is called Primary
Asset ID, which is one of those for
every single asset that you need to categorize. There's also this
concept of Rules. Rules are basically
sort of like the rules you apply to different
types of Primary Assets. It can be for cooking, or for
chunking, or for management. I will get into those
a little bit later. There's a Settings, which also
is very game specific, which lets you set how you load and
use each of the Primary Assets. And then, this
isn't a formal type. But we talk about
secondary assets because they're anything
that's not a Primary Asset. So that's like
textures or meshes. Because you don't normally
say, load a texture. You say, oh, this texture
is in a material, which is in a mesh, which
is in a character. So most things in the game
end up being secondary assets. Right, so to make a
Primary Asset Type, the easy way to do
that is you just subclass
UPrimaryDataAsset, which you can do from
both a native class or just in the Blueprint editor. That kind of does
all the work for you. You can also prototype
with any asset you want using some settings
that I'll get into in a second. But you need to
adjust your settings if you want to use that method
because it's a little slower. It's preferred to use
PrimaryDataAsset or something like it. You can set the scan options,
which I'll show you. And then the default behavior
is that the identifier will use the short name of your asset. So if your asset is
just called WeaponAx but it's a really long path, it
would just be called WeaponAx. It won't be called a
huge path WeaponAx. So that allows you to
keep track of things, but it also means that
you're generally not allowed to use the same name
in different directories for Primary Assets. And now we're going to jump
over to a bit of example. This is using the
action RPG sample, which is available in the launcher. This is a sample we
put out a few years ago to try to illustrate
how to use some of the more complicated parts
of the engine like the Asset Manager. And if it's an actual
small scale RPG. OK, so here is-- action RPG. Just to give you a real
quick look at what it is, I'm just going to
play out for a second. Oh, sorry, ignore that. So it's a simple top-down
Diabolo-like game designed for both mobile and controller. So you can see we have a weapon. We have an attack. There's enemies. And then as you play
this, you actually get to unlock new
abilities and new weapons, and each of those weapons
ends up being a Primary Asset that the Asset
Manager cares about. So going over to
our Content Browser, here's the assets in the game. And then, over here in items
are the Primary Assets. So let's look at a weapon. Each of these directories
has its own type. So like skill is a type. Tokens are a type. Weapons are a type. Here's an example, Ax-- so this has as an
actor that spawns when you equip the weapon, have
some inventory information. Kind of like a gameplay
ability that grants, which we talked a little
bit about I think last week or a few weeks ago. But the action RPG shows
how you might use abilities from the Asset Manager. For this example
that we're doing, I actually added
a few new fields that I'll show you
in code in a second. But the inventory texture
is an example of something that you may want to only
have in the inventory menu. You don't need it in-game. This pick up
sound is the sound-- you don't need this in
the menu because you only need to play that
sound when you're in the game to pick it up. This is another Primary
Asset that you can just pick off of a menu. And this will only give
you the Primary Assets that exist in your game. So these are all the ones
that you can select from. And this is an example of
data registry tag field, which I'll explain later. So this is how you
make a Primary Asset. But then how do you use them? So the action RPG has a few
simple examples of that. The game instance has it. OK, so here we're looking at the
game instance of the action RPG sample. So this is code that runs
whenever you play the game. In this case, this
initialised store items runs at the beginning
of the game session. So what this does is it goes
through a list of item slots that are set up. And the key in this map is
a Primary Asset type, so like weapon, potion, et cetera. Goes through that. Gets the type out of that. And then it calls an
Asset Manager function that gets every single Primary
Asset that is of that type. Then it starts an asynchronous
load of all those assets. So this will actually take
multiple seconds in theory. Then once this is
completed, that we'll hit this completed pin here. And you can see
that from the clock here-- the clock means that
this is a slow operation that will take multiple seconds. And once that's done,
it will actually-- it iterates over all the
items it loaded off disk. And then it can
cast them to a type. And then it add these
to the UI basically. So then the UI knows all of the
items that exist in the game, and then it can
show them in a list without having to hard
code a bunch of paths to all of your items. The other thing this
does is this calls a hacky example function I just
added for this stream, which I will talk about in one second. I forgot a step. The other important part is
setting up your settings. So that is just available
from the project settings. There's an Asset Manager
section on the left. It's a little hard
to read this, so I'm going to walk through this. But let's find a weapon. Here's a weapon. So this is the type definition
for the weapons we just looked at. So you pick the class that it
is, save as Blueprint only. The editor only is used,
in this case, for maps. I think maps are editor only. Yeah, so an editor only
type is one that is not available in a cooked game. So you can categorize
it, but only as far as like the
cooking stuff not as far as the in-game querying. This directory is a list
of where to load it from. So in this case,
this was selected out of the directory where we
had all the weapons stored. These Rules, I will talk
about a little bit later. But this is-- I'll come back to
Rules in a second. And then outside of
this types array, there's a few other
global options. You can actually set up some
specific overrides here, which allow you to-- which
I'll talk about in a second. Then down here are some
options that kind of allow you to use the
prototype settings. So by default, the
editor will just use these settings
up here and trust you know what you're doing. But in a cooked
game, it actually won't work unless you hit this
global option here, which says, don't listen to the items, listen to the Asset Manager when
deciding what type an item is. This is a little bit slower, so
we don't turn it on by default. But if you're doing a
Blueprint only game, this will be a useful option. I think that's all the
main options, yeah. All right, going back
to the presentation. So now I'm going to get into
the nitty gritty of C++. So the next, I don't
know, 10 or 15 minutes will be very code heavy. So if you're a designer, you
may not care about this part. But we'll get back to more
general stuff in a minute. So the Asset Manager,
again, is kind of like an Engine Subsystem. So you get it with
a global accessor. There's a bunch of functions
on the Asset Manager, which I'll go over in a second. The common one here is-- basically, this
GetPrimaryAssetIDList is the equivalent
of the Blueprint I showed before that gets
all the different weapons in the game. This is actually querying
the Asset Registry, which I'll show in a second. This does an asynchronous
load like the Blueprint that I showed before. And this will, in
this case, in C++, instead of using the
Blueprint things, you actually pass it
a delegate function. And then from your
Delegate Function, you can do whatever
you want because it's been loaded to memory. And then if you use
the load function, this will stay in memory for
the rest of the game forever, which is useful for
things like items where you want to
load all of them. But let's just say, you don't
want to load it forever-- you want to get rid of it--
then you can call the unload, and this will let it be released
on the next garbage collection. This will not
immediately release it. And if something else is
holding on to that asset, it will not get released either. But you need to do
this if you want to switch game modes
entirely and throw away all your old stuff. And then instead of using
the like permanent load here, you can also use a preload
and this returns a handle. And this will only
keep things loaded as long as this handle
is stored somewhere. And I'll show you
that when we go over to the code in one second. Yeah, one big concept
to talk about here is-- you see how this
CurrentLoadState here-- this is a list of
bundles, Asset Bundles, it is a more advanced concept. Not every game needs these, so
it's totally fine just a pass in an empty array
for those functions. But an Asset Bundle
is what you use to support multiple game modes
like menu, in game, and client and server. You can do this by like setting
up metatags on the actual UProperties in your C++ asset. And that exposes
it to the system. There's also a C++ function
on PrimaryAssetData you can override. And then there are
basically some functions in the Asset Manager that let
you change the global state. So you can do a query to find
all the weapons in the game. And then you can say, change
all the weapons from menu mode to in game mode. And I would release
the menu textures but start to load
the in-game sounds. You could also do this by
using another function called Change Bundle State
For Everything, and then that will basically
let you change the state for everything in your game. So that'll be for a
very large change. Before I jump over to C++, I
think now's a good time to talk through some sort of general
loading best practices because it's tied to bundles
and a few of those other things. And I'll show those
off a little bit when we go look at the code. But like I mentioned
before, synchronous loading is a bad thing to do in general. A synchronous load is when you
call just like a load object call from C++. There's a few Blueprint
nodes that do this. This could take a
pretty long time. Like this could take seconds. And during those seconds,
your user sees nothing. So it might look be
the game has crashed. In some cases, it might actually
fail a certification for you on console platforms. This is a pretty bad
experience to do that. So the general rule
that I like to use is only do this
during load screens or you can sometimes get away
with doing fast ones if it's in response to user input. So the user has just
changed a setting, then OK if it takes like
a second or to apply that setting because they
know what's going on. They know that they press the
button-- oh, OK, something is happening. It's probably because
I changed a setting. So that's OK. But, obviously,
if it's too long, it might be a problem again. You basically never want to have
a random stall of more than a frame or two in the
middle of your gameplay because it just feels
broken to the user. I mentioned the Asset Bundles. So the Asset Bundles
are a good way to handle loading
screens so that's usually where you would change the
bundle state and clear caches and release things. So you don't really
mess with that while you're in-game
unless you're doing something game specific. But in loading screens, you
can do whatever you want to. The StreamableHandles
like I mentioned have a bunch of
utility functions on them, that I'll
show you, that can be useful for just
managing things going on. There's two broad ways
to handle soft references when you want to load things. The first way is
you can basically say, asynchronously load this
thing, wait for it to finish, and then do a thing. So that's the example I
showed in Blueprint where I did the multi-second load, and
then it called a callback when it was done. So that's good for big things
or when you know you can wait. There are sometimes, especially
in the user interface, where that's not as easy to do. So sometimes you want to
use what's called preload and then a fallback load. So you would start a preload
when you enter a new menu. But then it may
not finish on time. The user may be
clicking really fast, and it may break
through your preload. So in some cases,
we do have a preload and then you fall back
to asynchronous load. Those are the two
general ways I would say it's good to do loading. Next one's kind of
one that just keeps coming up whenever I contract. So a lot of
programmers really like assigning lambdas to delegates. If you don't know what this
means, don't worry about it. But if you do know this
means, please don't do that. Or be very careful when you do
that because a lot of people write lambdas that are not safe
to execute a few seconds later. Be very careful when you use
lambdas on anything that can happen multiple seconds later. And the last thing
I'll mention is, the samples of existing
systems aren't really using all these
features to their best because a lot of stuff was
built for internal games, and we moved it
into samples later. So action RPG only uses
half of the functionality of the system. Everyone knows about
this, so we want to do a better job about
this in the future. It's something I
care about, so we're going to keep working on that. Actually no. We're going to go to C++ now. Sorry, my order is bad. OK, we're going to walk
through some actual code here, and make it
look nice and big. So here is the function that
the Blueprint was calling. Again, this is not a real
function in the sample. I just wrote this this morning. It's just kind of showing off
some of the things you can do. So this gets the list
of all the weapons. This does a Asset
Registry query. This basically, when you call
this GetPrimaryAssetData, it fills in this FAssetData
structure for you. And then you can query it
with a-- it's a key value map. And then this is
actually the example time I added to the
item Primary Asset. So this has existed in
the sample, and these four fields I just added. So this example registry
tag, you'll see, this has the data
AssetRegistrySearchable on it. And something that's
searchable, that allows you to query it later on. And then this is the ones
I showed in the editor with the linked Primary Asset. And then this is how
you set a bundle up. So this inventory texture is set
to only be in the menu bundle. And then just pick up sound set
to only be in the game bundle. OK, back to the example code. So this example code goes
through all the items in the game. Grab is this example tag. This just a little
helper macro that makes sure that this is really
defined in the item here. So it finds a tag named this. Gives that value,
throws it in this FName. And then we just print out
the FName as an example. This code here is the
same as the Blueprint node I showed that
just loads the assets. So we declare the PrimaryAssetID
of this weapon type. And this weapon type
is actually defined up in the header for
the RPG sample. This is a useful thing to do
in general just to have these ready to go for your game
because you're going often need to call these from C++. So it makes a weapon,
PrimaryAssetID. Then it loads it. And then this is the
callback I mentioned. So it basically just binds
this function to the callback. So when that load
finishes, it gives it to this code,
which, in this case, just prints out the name
of the thing that loaded. And then just unloads it. And then here's the
streamable handle I mentioned. The preload case,
like I mentioned, you need to use the
handle from this because if don't use the
handle, it just goes away. But this handle can be copied
around or stored inside like a global object-- you want to keep things loaded. And then this also just has a
lot of useful functions on it like I mentioned before. So you can see if it's
still in progress. You can get a name off of it. You can cancel it ahead of time
if you're like, Oh, I don't care about this-- stop loading. You can change the delegates
after it's already started. And then you can also, if
you wait until it complete, this will actually
stall the game thread until it's done loading
whatever's in the handle. Anyway, this just
gives you more options in you're game specific
code to do what you want. I think it's about it
for the C++ side of it. Oh, one other minor
thing, so this is kind of what the
settings ends up looking like in the INI file. So it's actually
sometimes easier to read the Asset Manager
settings in the INI just because there's
just so many fields. So you can use the
in-game interface, or you can use the in-game
interface to get you started, and then you can
just go into the INI and change a few things. You would have to restart the
editor if you do that, though. I think that's it for C++. We can come back to this later
if they're like more coder specific questions. I'll have some water. OK, so this is the loading
section of the discussion. Now, we're going to get over
to cooking and packaging. So this is something
that a lot of games don't even think about until
very late in the process. And that can be a
problem sometimes, so that's why I advise-- a good time to think
about some of the stuff is when you're like
turning your prototype into like a full
game, and you're getting ready to figure out what
platforms you want to ship on-- if you want to be on mobile,
you want to be on console. And then the Asset
Manager can be used to help you tweak the
rules for cooking and shipping. What you can do is you can
just override the modify cook function on the Asset Manager. There's a bunch of functions
on the Asset Manager, which I'll go to-- at the end I'll walk
through some of those. There's also a finish loading
function you can override. This is a good place to
just do game-specific stuff because anything that's
in memory when the Asset Manager is done loading
will get cooked. You don't even need
to reference it. It'll get cooked
if it's in memory. So this is a really easy way
to make sure things get cooked. You can also set up cook rules
in the INI, in the labels, or in the C++ code. And cook rules allow you to
override these rules per asset. You could do some kind of
complicated stuff there. And one thing we want to try
to make a little better-- it's not super accessible plugin
right now, at least in 4.26. And we know that
something people want. So that's going to-- we're trying to figure out
ways to do that better. So this is for cooking. So cooking is the process
of actually getting things from editor assets into
cooked assets, which is kind of like an
intermediate step because you don't generally
run from the cooked assets. You run from the staged assets. And then in chunking
is about the transition from cooked assets
to staged assets. There's a few ways
to do staged assets. The older method is
using dot PAK files. And there's something
called IO Store which is like a newer version of
PAK, but the same concepts apply for both. Basically you have
individual staged files, which have a bunch of
different assets in it-- thousands of assets
per staged file. By default, everything
goes into chunk zero. And if you don't have the
generate chunk option on, everything goes into zero. It doesn't matter when you do
it, it will all go into zero. But once you enable
that option, you might have a chunk
zero stage file. You would have your chunk
one stage file and so on. You can actually
make a hierarchy of chunks using an ionized
called the ChunkDependencyInfo. That allows you to say
that if something's in both chunk one and chunk
two, don't bother putting it into chunk two because chunk one
is like a higher level chunk. By default, chunk zero is
the highest level chunk, so anything at zero will not get
put into a lower level chunk. This can be useful if
you're setting up like-- if you're setting up a
platform specific feature where you want some of your
stuff to be downloaded at start and some things downloaded
as you play the game or in certain
languages, you may need to set up these
dependencies so that it doesn't like duplicate the
assets into multiple chunks. And again, you would need this
for platform specific features. If you're just shipping
on a standard PC platform with no special
downloader features, you don't actually
need chunking. But if you're on a mobile
platform or your game is large enough and
you want to do language specific downloads or texture
packs that kind of thing, then you're going to
need to use chunking to split your assets up. And also it works with the
new chunk of downloader plugin that was released in 4.26,
which I'm not going to go into. It's a whole complicated
thing on its own. But that's kind of how you would
use the Asset Manager to decide which chunks things go into
for the mobile downloader. Like I mentioned, you can
set up chunks and cook rules per asset. So you can assign things
directly in the config. So I kind of show that. We'll go back to the
Asset Manager config. The Primary Asset Rules-- you could say, for
the health potion, we're going to say that it's
a super-high-priority potion for some reason. And it always goes in chunk two
just 'cause we're saying that. So this is kind of like the most
specific way you could do that. Recursively doesn't
apply in this case because you're setting
it to exactly one thing. But the cook rule says
when you should cook it. So you could say never
cook, which we actually stop it from cooking. So this is useful if
you have something that you just want to make
sure it never gets out, ever. Always cook just
always cooks it. This is useful
because you can just put anything you
want to in your cook. Develop and cook-- this
ties into this option where you can say that
you have a prototype or development-only assets,
and then you could use that with a switch to decide
what kind of things go into your final cook. To use that, you need to turn
on this Only Cook Production Assets rule here. So anyway, these rules
here, I mentioned, are used throughout the
rest of these things I'm about to talk about. So you can it directly in the
config like I just showed you. You can also set up labels, and
then you could also override some C++ functions to change
how these rules are applied. This is a much
more advanced thing that I'll talk about a little
bit but some of these rules are complicated because
the engine is complicated. Specifically, it's sometimes
hard to tell what owns what. So it's hard to
know if a map owns an item or item owns a map. So that's one of those
cases where you might need to change those rules. I've go through a minor example. So going back to the example. So here is the Primary Asset
Rules like I mentioned. We're actually going
to get rid of that because it's not really
what we want to do. Instead, I actually set up some
labels in the Content Browser. Shooter Game also has
some labels set up, and that's how Shooter Game
does its chunk assignment. So for the action RPG, I
set up a main menu and then a gameplay map label. So this case, this
says that everything that is referenced
by the main menu map will be assigned to chuck zero. This does apply
recursively, so this says that everything under
this will get to chunk zero. And then I set the priorities
super high in this case, because we want everything that
it uses to be the main menu. Then the gameplay
map puts everything that's in chunk-- everything
else into chunk one. And again, chunk one is a
lower priority chunk than zero. And then I also set it
up in the config already so that all of the items
like weapons and stuff have a chunk ID set to two. So basically, any
item or potion that's not used directly to one of the
maps will end up in chunk two. Yes, so that's how
you set of rules. And I'll show you how you
look at those in a second. So all these things are kind of
hard to get a grip on until you really understand the system. Like, it's kind of hard to
do it right the first time. Don't expect anyone to do
this correctly the first time. So there are some tools
that I and some other people built to help you investigate
what your assets are doing. First one is the
Reference Viewer, which is a useful
thing in general that I'll show you in a second. That allows you to see what
assets refer to other assets. You can also use the
Asset Audit Tool, which is just kind
of like a list view of all of your assets. Kind of similar to
the Content Browser. And here's some options
you can do on there. And then there's
also the Size Map, which allows you to get
a hierarchy view of all of the assets in your game. It come's from the
top down, which lets you see what's using that
super big texture that you don't want to ship. And that's actually-- if
you saw the announcement for this, that was
the size map which I'll show you in a second. So again, these aren't really-- it's kind of hard to
talk about, so we're going to go through them. I should leave that up. So to get to the
Reference Viewer, the easiest way to
get there is just find any assets you care about-- so let's go to one of the items. Sorry. Let's go to Reference Viewer. I could see this view here,
could make it a little bigger. So this is a useful tool
for all sorts of things. This let's you see
that the ax is using this gameplay based class-- this is ability, this actor
that spawns, this texture. So these are hard references. You can tell because they
have this white line. And they stay if you
disable soft references. These purple lines
are soft references. These are the ones
I added to the demo that you had to use
bundles to look. The other thing you
can do, is you can turn on management references. So this should not turn to none. That is a bug in 4.26 that
should be fixed in the future. But you can still see
the name above it. So this is the ax. You can see that the ax is
managed by this Primary Asset-- Weapon:WeaponAx. So that lets you see that this
individual on-disk asset is logically owned by this
Primary Asset name over here. This is the Primary
Asset identifier. So this is the actual
Primary Asset identifier. Now, if we come
under that, you will see that this is
assigned to chunk two. And that is because
of the rules we set up in the private
settings where all weapons are assigned to chunk two. So you can use this interface
to navigate all around and figure things out. The next tool that's
useful is the Asset Audit. You can get to that two ways. You can either just go up here
to Window, Developer Tools, Asset Audit-- oh, it's already open. Or you could actually
select the assets you care about, right click
them, go to audit assets, and that'll put it over
here into your audit view. And this is basically just a
version of the Content Browser that is meant for just
looking at things in detail. So there's no big
images in the way, there's no folders,
that kind of stuff. So yeah, you can see details
about using these weapons. You can actually see the
Asset Registry data shows up right here. So because I have added
to this example registry tag for this particular weapon,
that just shows up here. You could also see what
chunk it gets assigned to, the cook rule for it. This usage is a
little complicated. This is just kind of like
how many things are using it multiplied by their priority. So it's kind of an
abstract concept. It's basically like,
how important is this? Bigger numbers are
more important. So if it's like a million,
it's probably really important. If it's one, it's
probably not important. If it's zero, it's actually
just not used at all. And then this disk size
is how big it is on disk. Exclusive means just how big
is the actual asset on its own, which is very small
because these are just like tiny little data assets. But the disk size overall is
all of the things it manages. So this includes all of the
textures and meshes used by it. And then here's the name of it. Yeah, from here you
can right click things, and then you can get to the
other tools like the Reference Viewer and the Size Map. Another very important thing
you can do on this view is this dropdown over here. This is probably hard to see,
but this is actually selecting which platform you want. So if you have locally
cooked for a platform, it will show up here. Or if you haven't, you
can actually go to custom and then you need
to find basically the development
asset registry file. So this part is in
the documentation. So the details
about this part are described in the documentation. But if you go over here and
select the cooked platform, that actually switches
all these sizes over here to be the cooked sizes. So these are much more accurate. So the editor sizes-- it does its best to
guess how big it'll be. But it's really
not that accurate. But this is how big it will
actually be in your data, uncompressed. So it'll be a little bit
smaller on disk usually depending on what you're
doing per platform. But the point is you can
use this to figure out how big things are. So you can see from this that
essentially the ax is four times as big as the hammer. To see that better, we
can use the Size Map. So this is-- that's not
a great view actually. I'll just get all of them. OK, so this is looking at
all the weapons in the game. This view is hard to
understand initially. It's kind of intimidating. The important part is the bigger
the box, the larger it is. So this particular texture is
very large for some reason. This particular
sound is very large. But these pictures over here,
kind of small, who cares? So there's a shared
section up here. This is all the things that are
shared amongst all the weapons. And you can see that
there's a lot of things that aren't shared. And you can actually see
this because like the base Blueprint weapon
actor has references to a bunch of other things. But you can use the mouse
wheel to zoom in and out. So I could zoom in to this
section of wave progression data table. And you can zoom back out. On the right here, are
the individual weapons. So we saw that hammer
three was really big. So hammer three happens to have
these three giant textures that add up to 3.6 megabytes. So hammer one for some reason
doesn't have any big textures, so this one is tiny. So hammer three is a
thing you should optimize. Whereas, hammer one, you
don't need to worry about it. But this is a really
easy way to figure out what kind of textures
and meshes should be optimized by your artist
because artists tend to-- or you can go back to
the Asset Audit view. Because in the Asset
Audit view, in addition to having these primary types
that I brought over manually, you can do clear up here. And then you can
do add asset class, add all textures in the
game, all 2D textures. OK, so this is every single
texture in the sample. And we're just going
to sort by disk size. So we can see that the
barbarous character texture is very large. And you can see
this because it's a pretty big normal map for this
character, so that makes sense. But if you go back to
the audit, some of these don't make as much sense. So here's a good example. In this case, the Speed
Tree Game Wind Noise texture is five megabytes. I don't know why the Speed
Tree Wind Noise Texture is five megabytes because it's this. But yeah, just for
some reason using-- this is part of the speed
tree importer apparently. You can see the path
was in the importer. What else is there? Mostly it makes sense though. The textures is kind
of a bad example because I think
this game actually has been optimized pretty well. But if your game has not
been optimized at all, you might find that you
have some incredibly enormous like 4K textures
that are plain colors. And you don't need that. So this view is
a really easy way to find your low hanging
fruit for optimization. Another thing you
can do in here is-- clear everything out. I'm going to do, add chunks. So this just gives you all
the chunks in the game. So again, zero is kind of like
the main menu and everything that the game startup needs. One is all the stuff in-- actually you can just
see these if I just go to be the Size Map. So one is all the stuff inside
this action RPG main gameplay map. So you can see, this map has
a lot of textures it uses and so forth. But this is like the
lighting data, right? So the lighting data ends up
being pretty big in most maps. And then chuck
two is going to be all of the individual
Primary Assets that are not included
somewhere else. We already saw this view. Basically, it looks like
all the weapons and stuff. You might notice the chunk
zero is pretty big, right? And you want this zero
chunk to be small. If you do Size
Map on chunk zero, you can actually
see what's going on. So over here, you'll see,
again, here's this big Wind Noise Texture. This is not inside a
label or anything else. That usually means that the
engine is referencing it or it's just loaded at startup
or something like that. So this might be something
you need to fix in C++ code or so forth. So here's the default font. You probably need that though. The logo, I think the loading
screen uses this logo, so that's why it's like big
and not inside anything else. But you can also see that this-- the main menu label,
this is all the things that the main menu map needs. And again, there's a
lot of stuff in here. This also includes all
of the base Blueprints. So this is a good
way to figure out your base Blueprint is
huge for some reason. The player character is 50
megabytes of total stuff, and that's probably because
like the player character references-- [INAUDIBLE] you
just figure it out. So why is the player
character so big? You can see here that
the Goblin Level One Blueprint is referenced
by it for some reason. And then you can see-- we're back to the
Reference Viewer. And you see there's
a hard reference from the player character
Blueprint to the Goblin Blueprint. So that's an
example of something that you may not want
in your shipping game. Because your player's
loaded in every scene, but you may not have goblins
at every single level. So this is the
kind of thing where you might want to go back
later on and use the Asset Manager to figure out if maybe
you want to have an enemy Primary Asset type. And that would allow
you to get rid of this like hard dependency
on the Goblin. So why is the goblin
referencing that? Let's see if it
shows up somewhere. It does. Yeah, good example. So in this case, the
goblin is in memory because of a debug function. So this is just a function
that was added for debugging during development. This doesn't even work in
the actual package game. This will be false, and
it won't do anything. But this Blueprint node still
exists in the shipping game. And this Blueprint node
references this goblin. So this debug
function is resulting in something like 20
megabytes of extra stuff ending up in chunk zero. So yeah, your game will have
a lot of issues like this. There's a lot of things that
can cause hard references. But these kind of
references are again why we built the Asset
Manager in the first place was to kind of try to deal
with complicated use cases like this where we don't
really want to load everything. Let's go back to the size
map, and see if there's anything else interesting here. Not really. I mean like, here's a
player character that big but that made sense. The item Blueprint
has some stuff in it. Yeah, again, action RPG
is pretty reasonably well optimized because
we wanted to make sure it would work on mobile platform. But if you have a prototype
game and you go to this view, I guarantee you
you're just going to see like crazy things in it. You're going to be like, oh, our
whole entire game is just one texture. So like you can get
pretty far to optimizing your game by just
using this view to figure out what's going on. OK, I think it's all the cool
stuff I want to talk through. There's a few other
options up here again. So you can add all
the types if you want. You can add every single
primary asset if you want. Here's all the maps and
the weapons in the skills all in one place. We go back to my presentation. I don't think I have much left. Oh, I do. A few things, I'm just going to
kind of mention this quickly. This is just ideas to think
about for advanced programmers. This is not something you
want to dive into quickly. These are things that you
can build on top of the Asset Manager to do. So you may want to--
like for instance, you might have like a
lot of complicated things when your game starts up. Most games just start by
referencing everything and loading everything at once. But that may not be
something you want to do. You may want to have a loading
bar like a progress bar or something. So you could use
the Asset Manager to like build a little mini
state machine to give yourself a progress bar. One thing that ends up
being pretty helpful is when you do a map change-- so the map change
itself is going to be a hard load
generally unless you're using certain things. But most of the time, it's
going to be a hard load. But you can make that
road a lot smaller if you do like a preload of
all the assets the map uses. So you could write
some custom game code to do that with bundles
or just do manual stuff. It is hooked into
the chunk downloading system used by the chuck
downloader plugin I mentioned. So that is in the
battle breakers game a mobile game that
we have shipped. That's actually set
up to use the Asset Manager to download chunks
off of the web on demand. So you can actually say like,
async load this character. And that'll start a download. And then I'll go to
server and download it. And that download will finish. And then it'll load
back into your game, and give you a call back
saying here's your asset. It's now in memory. Again, it's not really
a good example of this. It's a little complicated. But all the kind of like
plumbing pieces are there. You would just have to
work through those pieces on your own. There's also some kind
of custom reports. That's a useful thing to do. The Asset Manager
has a few built in. And actually, before
I go to the Q&A. This is a good time to
do one more code thing. Yeah, correct a code. This is a visual assist,
which I'd recommend. OK, so the Asset Manager
header file-- like I mentioned, it's kind of complicated. But I've tried to put
good comments on here explaining what's going on. There's a lot of virtuals here. Basically everything is virtual. You can override pretty
much everything in here. So it's kind of
organized by section. So it's like general
utilities for getting stuff. There's sections
for async loading. There's sections
for bundle changing. Again, these are
very complicated, so they have very long comments. Here's some of the stuff on the
chunk downloading down here. OK, there's a bunch of general
utility functions here. So these are useful for
pretty much every game. So, yeah, Get Asset
Data For Path-- this will just give you a
object path, like a soft object path, which is the same thing
as a soft object reference. This just goes to
the Asset Registry, just gets you the
asset data for you. And this allows you to ignore
like redirectors and Blueprint assets, that kind of thing. So all these utility
functions are just designed to be easy
to use on purpose. It's kind of the opposite. So this takes like
a asset data you want to get out of
the Asset Registry. It tells you like-- this gives you a pointer to
the actual thing you want, which, in the Blueprint case,
will be a Blueprint class. If you just create the
asset register directly, you actually get back to the
editor Blueprint object, which is almost never what you want. So you want the
Blueprint class mostly. So that's why the Asset
Manager has some wrappers to avoid that weirdness
of Blueprints. Here are some of those
report functions I mentioned. So one of these goes through
and gives you actually like a-- this one gives a list of
all the things referencing like a package. You can either call
that from a script, or you could just have
your own function defined in your own Asset Manager. And down here, the virtuals
you want to override so here's the one that called
at the end of loading. There's just a bunch
of other functions. Like here's a modified
cook function. Yeah, you could
directly override which chunk things go into if
you want to do something weird. Again, there's just a
lot of functions in here. So once you are
at the point where you've done the basic
Asset Manager set up and you have your
basic type set up, that's kind of a good time
to scan through this header and see if there's anything
interesting you might want to use for your set up. There's a lot of functions. I think it's all the stuff
I wanted to get through. So I'm going to get
some water real fast. I think it might be a good time
for Victor to talk about Q&A. And I'll be back in one minute. VICTOR: For sure. Also time for me to address
some of the audio issues I was having here. I was unable to speak throughout
Ben's presentation, which is fine because he clearly
doesn't need me helping out. While Ben is getting
some water, I guess I should switch
this over, and then I can let you all know that next
week, we're actually going to-- We're going to do Q&A. I'm
not ending the stream here. I know this is usually what
I say when we end the stream, but next week we're going to
cover the Chunk Downloader. Michael Prinke
is coming back for another livestream,
which is exciting. And yeah, I hope you're all
are excited about the MetaHuman Creator. Been sitting on
that for a while. And it is absolutely
amazing what the team has been able to accomplish there-- super excited about that. I'm personally not very
familiar with the Asset Manager, and so I don't
unfortunately have anything I can share with
you right then or right there about it. Anything else new,
cool, and exciting? Let me take a look at the
upcoming schedule for those of you who normally tune in. And I can give you a
quick brief of what we have coming up on the channel
in the next couple of weeks. One would think I
noticed by hand, but there's a lot
going on to it. So next week, we're covering
the Chunk Downloader. Week after that, we are
actually bringing on a team from Germany called
On Point, and they're going to talk about their
virtual theater setup. So there's a little bit
of virtual production going on there. And then week after
that, we're doing a simulation and training,
which is going to be exciting. I also have Nvidia covering
DLSS in the future. BEN: I cannot
hear you Victor. VICTOR: That is because I am muted. And I'm going to tell Ben that. I had to go through a
couple of different-- there we go. I had to go through a little
bit of an audio change set up that I discovered we had
a problem when we started to stream because of the
shift of which PC was actually hosting the stream. I just want to
verify that everyone can hear Ben now as well? BEN: Can you all
hear me on the stream? VICTOR: Yes,
it looks good here. And we should no longer
have you double-- me streaming out twice. Thank you, chat. Good to see that everything
is working as intended. Awesome. I was just covering
a little bit what we were going to
show on the channel in the next coming weeks. But I think it's time to cover
a couple of the questions that we received so far. BEN: Yeah, absolutely
and I can kind of-- one thing I will say
is, I'm not going to go into detail
about the specific ways that Epic has used
this in the past. Sort of be more
general talking about how a game would use this, or
how games in general use this. VICTOR: Sounds good. Sounds good. Start from the top. matzeogh asked, is it
possible to cook and build a specific chunk? For example, having a project
with HTTP chunks enabled can be quite slow if you only
want to update one chunk. BEN: Can you
cook a single chunk? VICTOR: Cook and
build a specific one, yeah. BEN: I don't think
you can do that actually. No you can definitely
write some custom code. So you could modify
the Asset Manager to just skip
everything in chunk zero. That would kind of be
like never cook stage. But I don't actually
know if that'll work. One thing you can do, you
can use the iterative cooking feature sometimes. But I wouldn't recommend
doing that for a real build. Like for real builds, you want
to cook everything from scratch because sometimes we
have issues where like-- the problem is that sometimes
when you cook something, it actually changes
it in ways that are kind of hard to predict. So it's better to do better
to cook everything the same. But you can set
things up so that you cook using a baseline, which
is similar to a DLC cook. It uses like half
the same options. That will just try to
make the data you put out as similar as possible
to the original data. So the download will
be small, you still need to cook the
whole thing, but then a download will be much smaller. VICTOR: Brainshack
asked, can the Asset Manager be used to group content
for DLC and similar that can be downloaded on demand
to keep package size small? BEN: Yeah, basically. So yeah, that's kind of
what I just talked about. So the process of doing
that is hard to describe. There's kind of two
ways to do this. For most games
where you download things from a mobile
game or so forth, you probably don't
even want to use DLC. You probably just want
to use the method I just talked about where you use a
baseline package build and make a patch off of that. And then you would
just ship your chunk zero directly in your
download on Google Play Store or something. And then you're like,
chunk one through 20 could be like stored on
some other download server or using Google's system-- I don't know how that works. But the point is you can--
for most games you probably don't want to use DLC explicitly
for things with lots of chunks. But if you are using
DLC, you definitely can use chunks to
set that up as well. But I've not actually
done that myself. So I have not tried to combine
DLC with chunk assignment. I've heard from
some licensees that this does work fine. But I've not tried it
because the DLC cooking makes it's own PAK file anyway, which
is separate from the chunk assignment system. So I think you
probably want to use either chunk assignment
or DLC, but I could be wrong about that. VICTOR: Next question
comes from Willowulf1. They ask, does the Asset Manager
support saving slash loading per instance of item data. For example, a sword item
that has a random stat range-- plus one to 10-- strength that must be
saved per item instance. BEN: No, it's does not. No we don't have-- the
Asset Manager is designed to be used with Editor Assets. I mean, you can definitely-- you can get pretty close
with like-- a Blueprint inheritance gets you're
pretty close to that. But no, it does not
save the actual data. One thing you might
want to look at is the action RPG save game
code in the sample. So that has a really simple
inventory saved in it. But that uses the
Asset Manager to get the list of all the items. And it saves a inventory by
pointing to the Primary Asset ID. Then it would save the
random data on top of it. And then there's some
code in there to reconstruct
those items for you. That's generally the way to go. The engine doesn't
really have a built in instant save and restore
thing built into it. VICTOR: I will
go ahead and add, for a simple version of that,
we do have the simple UVC plugin that Chance Ivey covered
on the livestream last year. That is say, an
easier way to continue to push DLCs et cetera
because they can essentially be loaded as a mod. And this is without
the whole end user experience of being able to
actually modify the game. But you can actually
use that plugin to deliver content as a DLC. It will be called a
mod in the back end. But for the end user, it
will actually just be a DLC. I will go ahead and
link that in chat. BEN: Just to
mention, in general, sometimes the code
refers to mods and DLCs kind of interchangeably just
because of historical reasons. But also a DLC-- DLCs, mods, and plugins are
kind of all the same thing depending on how you use them. So sometimes it might be a
little confusing in the code. VICTOR: It's all about
understanding it, right? Being able to work with it. Let's move on to
the next question. ryobg asked, how to avoid
pulling in references when casting in Blueprints? Soft loading usually
causes return of generic objects, which
needs to be casted and stored in member objects
slash class references. How much is actually that
nullifying the benefits of delayed and soft loading? BEN: So yes,
that's a real issue. Let me construct an artificial
example in the editor. Yeah, so let's go
to this Goblin. OK, so we're going to
add a soft reference-- I'm sorry. This is kind of on
the fly so one second. VICTOR: Doing
it live, folks. BEN: Yeah,
here's an example. So you do the async load
here, of all the items. If you have an async load that
does-- so everything up to here is fine. But then if you do a cast here-- cast to goblin, range
one or whatever-- yeah, the cast to goblin
NPC level 1. So this is bad. So even if this object
was asynchronously loaded or is this is a soft
reference or whatever, every cast node
just does it. You have to have a
full reference to it. So the general fix to this
is, you just never do that. You only ever cast to like-- what's the parent
class of goblin? BPME character. So basically, you just only
ever cast to your base classes. And then, usually,
base classes will not have a lot of Asset
References in them. So if you go-- because obviously this has
a lot of functions in it. This has variables. It has components. But the class defaults are all
in the component out there. Yeah, so usually they're meshed. So in the base enemy character,
there's no skeletal mesh there. So you want to do
as much as you can where you have an empty
parent Blueprint that would have all the stuff you need. But then don't fill in
things like skeletal mesh. And then in your
children, then you end up setting skeletal mesh
to this like actual Gruntling mesh. I go over this a little bit
in the like using Blueprint and C++ documentation page,
which I don't have a link for right now. But I can find one. But the point is, you want to
use Blueprint based classes everywhere. The other option you
could do is interfaces. So Blueprint interfaces
can also be used for this. They have their own problems. I would recommend using base
classes whenever you can. And then when you can't
because you have-- if you want to interact
with both an enemy character and like a chest,
then you can't really have a base class
because they're two totally different things. Then interfaces
are better for that than casting to like some
really low level thing. VICTOR:
There's a great video that I believe Zak Parrish
did a couple of years ago on Blueprint Communication. If you are unfamiliar
with the different ways to communicate
between Blueprints, go check that out on YouTube. It has taught me a
lot back in the day. Let's go on to the next one. I just want to say, interfaces
are absolutely fantastic. natnatrog32 asked, what would be the best way to deal with a
massive amount of spells slash abilities, perhaps thousands? BEN: The system scales
up just fine to thousands-- tens of thousands, fine. The actual problem
there is you need to have the assets
in the editor. There's two ways to do that. Obviously, there could literally
just be 1,000 weapons there. At a certain point,
you're probably going to want to write a
game specific tool to create assets for you. You could use something like a-- I think the Blueprint utility
widgets can do that, I think? I've never tried that with them. But I think they can do that. Or you can write game
specific ones to do that. You could use that to generate
a bunch of things for you. That's one option. I don't recommend
doing that unless you-- I don't know. It depends on your game. The other key areas
you're going to want to use Blueprint
Inheritance at that point. So these are not Blueprints. Yeah, these are not Blueprints. These are just pure data assets. But if you have a really
complicated hierarchy of tens of thousands of items,
you probably want inheritance. The other thing you can
do, you can use data tables for that, which might be
a better idea at the tens of thousands level
because then you could generate your data into
an external tool like Excel or even just like some custom
thing like custom JSON thing. Then like import
it as a data table. And then a lot of
games do is-- one thing you can do that works
really well is like mix and match Blueprints
and data tables. So you have like a
generic weapon Blueprint. And then on the Blueprint,
from the data asset or whatever, you would have a
reference to a data table row here. And then you would pull the
stacks out of the data table. And then by mixing
and matching, you can then say, here's a sword. So there's all the
sword animations. But it actually uses the
stats out of this data table. That's a better way to get
to like huge amounts of data in my experience. VICTOR: All
right, next question comes from KaosSpectrum. How would you search
slash query Primary Assets during runtime slash in
editor using gameplay tags? I like to use gameplay tags
to mark my Primary Assets. BEN: Yeah, a
few ways to do that. Let's go to the code. Right, so this code here-- here is like the
name example right. But you can just pull out
of the raw string you. Can pull everything
out as a raw string. So you could have like string,
and then you pull the tags value to there. And I'm pretty sure
gameplay tags have a from export text
on them because this is something we actually do
in some of the internal games. Yeah, here it is. Yeah, so you can just
call this function, and pass them to
the exports tray. I'm pretty sure that'll work. So then you have a
proper gameplay tag here. This will only work if it's
actually in the dictionary. But if it's in the
dictionary, you can then use this time to
do whatever you want to. So you can set up a runtime
map from gameplay tag to PrimaryAssetID or
gameplay tag to Asset Data. I'm actually working
on something for 4.27 that might help a
little bit with this called the Data Registry that
has native tag support built into it. It does not currently
interact with the asset manager in this way. But I want to do
it at some point. But yeah, it's a
pretty common desire to want to go from
gameplay tags to assets. And I think that's
probably something we're going to look at making
a real feature in the future because I've had to write that
in a few different times-- different projects. So it seems like a
pretty common thing that we should just
do once somewhere. VICTOR: All
right, next question comes from flassari.
Is there a way or plan to make chunks addressable
by string names instead of integers? There's definitely a plan, BEN: Yeah. There's not a great way to
do that just because of how we're-- chunks are stored at a very
low level in the system where space is at a premium. There's not a great
way to do that. I think you might end up
wanting to do something that kind of generates stuff for
you from some description file. Because you could name the
chunks after the Primary Asset in mem or something
without it being specific. So there's nothing built
into the engine to do that. For some games
I've worked on, we can set it up so you can
have a separate description of your top level structure. And then use that to like
kind of override some of the GetChunkID functions. Because on Asset Manager,
all the chunk functions can just be overridden. So like-- yeah, so
like this function, you can override this. So you can have this go into
your game specific thing, and then figure out, oh,
this is character Bob. And character Bob
should go in chunk four. You could do a mapping there. So no, there's no way to
do that to the engine, but that's also something
that has come up a few different times. So it might be
something we look at. But there's no plan
for that though. VICTOR: matzeogh
had another question. Extending the UPrimaryAsset
label with a new class breaks instances
of the new class, they get ignored by
the Asset Manager. Is it possible to extend it for
adding some metadata for DLCs and stuff? BEN: I would have
expected that to work. As the labels are kind of
treated like slightly weirdly, there might be something
specific to them where that doesn't work. That works fine for
everything besides labels. So I mean, that might just be
a bug in the Asset Manager. I've never tried
that, so it's probably just something we just doesn't
work for no good reason. That should work. So and if it's a
simple change, it seems like something like
a pull request that we-- I would probably put the pull
request if it's pretty simple. If not, maybe just describe
the problem better somewhere. I'm not sure. VICTOR: Next question
comes from DarknessFX. Can we use modify cook to split
builds like ASTC only, textures and materials
for Android, arm64-v8, and ETC2 only texture
materials for Android RM-V7. BEN: I think you'd might
want to use the other function in here, which is similar. So you do have some platforms
that are control. You can change the
chunk assignment here. So these chunk functions do
pass in the target platform, so the default doesn't
do anything with them. But you can definitely do that. So you can strip
out certain textures if you don't need them
on a certain platform. I think there's this
simple override here which just lets you exclude
certain assets per platform. Yes, I think the way-- yeah, I
think the way you do that is-- the easy way to do that
is to override the chunk assignment per platform. So then you would shove all of
the whatever format you care about textures into one chunk. And then your packaging layer,
you could just like say, don't package chunk five
on iPhone or something. I don't know how to
do that super easily. There is one file that might
help a little bit with that. I forget what it's
called right now. No, it's not it. I'm trying to remember-- I don't remember offhand. There's an INI
file that also lets you assign nonpackaged,
nonasset data to chunks, which you might need to do for
shaders and stuff like that. I don't know if
we have an example of doing that right now. This is another
example of a thing that we should have a
sample for but don't, so-- but it's definitely doable. VICTOR: Next question
comes from RimaWrench. Does Primary Asset Bundles
support TArray and TMaps of soft object pointers? BEN: Yes, that
should all work just fine. See where that function is. So it just calls the
Primary Data Asset, it just calls this generic
function on the Asset Manager. So if you don't want
to use Primary Data Asset for some
reason, you can just like override these four
functions in your base class, and that's all you need to do. But this generic function, I'm
pretty sure handles arrays. Yeah, it does. So it uses a property
value iterator, which actually will go to
structs and arrays for you. So it doesn't matter
where you put it. As long as it's inside
the same UObject, it should get parsed
out correctly. VICTOR: I have another
question from DarknessFX. Can we use Editor Utilities plus
Asset Manager or Asset Tools to automate editor tasks with
edit and save assets data-- like set everything from one
folder to a different chunk ID or apply new tags to Assets? BEN: Chunks, no because
the chunking system right now is more rule-based. You could definitely
write something to modify your INI file. But there's no way to do that
from Blueprints right now. There's not super great
support for Blueprint Utilities and the Asset Manager. I don't think there's much. There's some asset registry
stuff, I'm pretty sure. I mean, in my experience, you're
just going to have to write C++ code at some point to
do that kind of thing. I feel like when it comes to
things that are for packaging and shipping, you're going
to be in C++ eventually. So there hasn't
been a lot of effort to support packaging shipping
stuff in Blueprint Editor Utilities. VICTOR: Next question
comes from henrysdliu. Is that possible
to hold some assets which we do not want to be
unloaded during change map-- for example, a
bunch of textures. BEN: Yeah, totally. So that's why the
preload is useful. So actually, if you just do like
this preload Primary Assets, there's also just like a
function on the Asset Manager, which gives you [INAUDIBLE]. So you just pass it like a bunch
of solid object paths here. If you figure out where
all the textures are, you could just use this. Sorry. Start to go a little
slower on this one. It's complicated. You figure out what you want
to load, parse it in here, and then just hold
this handle around. So put it under game
instance or somewhere. And as long as it
handle is around, those textures will not
get garbage collected on the map transition. This is also very useful if
there's anything like a reload. So like if you die and want
to reload a checkpoint, this is about a very useful
thing to do in that case. So it doesn't matter that
we've got this already loaded. That's fine. So you can just kind
of ignore the delegate. You don't have to parse on a
delegate, so you just load it. I mean, just keep this
handle, and then you can do exactly that. Or if you do something
with bundles, that's why I was talking about
bundles, where you could set up like a map Primary Asset. You have like a bundle that
has all the textures it needs. That's more complicated, but
it's kind of the same idea though. You figure out what
textures to assign to it. And then you could override
the bundle functions to assign it to it. VICTOR: Next question
comes from CyberWolf755. How would you cook
assets and load them from a server for
a finished game? Does UD-4 support
downloading natively? BEN: Per server? No, I don't think
there's any support for server side downloading
in anything we've shipped. Maybe you could possibly use
the chunk downloader plugin. But I haven't tried to
do that on the server. It may be possible. No, I don't know of a
good way to do that. VICTOR: This
is a long shot, but I will try to do a shout
out to one of the teams in the 2019 Epic
MegaJam who's actually submitted a game
for the Jam that had a downloader built into it. They were very clever
about that because they're trying to get around one of
the special modifier category requirements. It's real clever. If you are watching or
might be in the future, it would be great-- I think those folks actually
wrote up something about that. And I am just not
sure where that lies. But they were able to
do it for the MegaJam, so I definitely
know it's possible-- 100% custom though,
as far as I know. ChaosSpectrum had
another question for us. I have a couple of things to
write for a Primary Asset-- item to world to
weapon to ranged. If I have a custom asset type
or ranged, I get a warning. Is this just a limitation? BEN: I don't
understand that question. That might be a better one
for some text later. So maybe post that on-- now's a good time
for a good unplug. The Unreal Slackers Discord
has some pretty good channels for like C++ and
more advanced stuff. So some of these more
advanced questions might be better off
just doing over there. VICTOR: We
can also discuss them in the forum announcement
post for this live stream on our forums. And then it's a little
easier to search for it once the conversation
has happened. But Slackers is a great place
to discuss and hash things out in more real time,
which is a little different from the forums. But Ben, I'll make sure
to send you the questions after the stream as well and
you can go ahead and tackle them when you have some time. BEN: Yeah, so I'll make
sure to check the announcement thread a few times this week. I mean, I won't
check it forever. But if you do post
specific things, I will definitely
take a look at them. And I'll answer them if
I know them if I can. VICTOR: I will check it forever because I get notifications
every time someone writes there. So then if there's
something that comes up, I will let you know. BEN: Sure. VICTOR: Let's see,
CyberWolf had another question. Does patching a game mean
swapping the changed chunks? So for example, if a game didn't
make enough chunks or any, it would result
in larger updates? An example being Mortal
Kombat 11 that had a PC patch the size of
the whole game. BEN: Yeah, there's
a few things there. I'm not going to speculate
about what they're patching architecture is like. But the full size downloads
are often because-- if you're not building with
a baseline release build, especially on older
versions of the engine, it would do a pretty bad
job of lining things up. I think it does a better
job now by default, but it also does a much better
job if you use the baseline. So that just should be better
in the more recent version of the engine. But it is true that if
you rearrange your chunks massively-- if you just move everything
and chunk one to chunk two, move everything in
chunk two to one, you're going have to redownload
everything if you do that. So you want to get your chunk
assignments figured out kind of before you release for real. Like if you have early
access or like weight data, that kind of thing is
when you want to finalize it. Because you don't really
want to change it after you have a lot of people
downloading it. VICTOR: saxhack
asked, does this work with the GooglePAD plugin? BEN: I have
no idea what that is. VICTOR: Me
neither, actually. I have never heard
of that plugin. BEN: There
might be the DLC-E-1. There's not really native
integration with this stuff. But again, this just makes the
correct staged files for you. And then the staging to delivery
is all very platform specific. And there's not really
unified way to do that. So it's all very different
depending on the platform. VICTOR: iSpamForFood asked, on a Data Asset, can
we soft load them and access something like a struct? Example being, I have a
data set with conditions. I want to access to conditions
without fully loading the data asset. BEN: Yeah,
you do something similar to what I just did
for the gameplay tag example. So this FromExportString
on gameplay tag basically does that for you. So if you were to tag
an entire structure as AssetRegistrySearchable,
pretty sure it writes out the
whole structure. Yeah, it does. And then you can
just kind of-- you have to use this weird
import text thing. But import text basically-- this is the syntax you get
when you copy paste things in the editor, basically. So uses that syntax. So you can have whatever
you want you in there. So you could totally just
have like a Rule structure in your Asset Registry
Data, and then pull it out with this import text. This would be kind
of slow though, especially if you
have a lot of these. VICTOR: SniperEcho
asked, I'm working on a project and currently exploring how
to implement delta patching. Is this a viable replacement
to delta patching? BEN: No, again,
this builds into that. As long as you have
your chunk set up the same before and after
with the delta patch, then it'll do a delta patch
for each of the chunks. So no, it's a complement
to the delta patch. VICTOR: SionSheevok had
a pretty good question here. Could you lend some insight
into using Primary Data Assets References outside
of the engine? For example, a back end service
with persistent inventory. Would Storing the string
like Primary Asset ID in the database be appropriate? BEN: Yes,
that was actually one of the reasons
we came up with that. I left it off my
slides, but yes. That's absolutely what
they're designed for. Because they're shorter-- so
you don't want to do the full path-- you can if you want. You can override this to use
a full path as the primary one if you wanted to. But in my experience,
designers like to move things
between directories more than they like to
move things, rename them. The other thing you can
do, is you can actually set up in your game so that
keeps the Primary Asset consistent over time. So you could override
some of these functions. There's a simple redirector in
the settings, I'm pretty sure. Yeah, so these Primary
Asset ID redirects, that's literally
for this exact case where you have a database
where you store everything as like Weapon.Ax, and then you
decided to call it Weapon.Ax4. Then you could set
up a remapping here. And there are some functions
on the Asset Manager that let's you do
that remapping. VICTOR: And this is--
we have a little bit of time-- there's a question here
that's sort of on topic, but I figure we can address it. What was that? BEN: Yes, go ahead. VICTOR:
sorryforthebadpuns asked, what are the kinds of
problems associated with using interfaces
instead of Blueprints to avoid hard Asset References? I think what they're referring
to is using Blueprints, or sorry-- interfaces instead
of casting in-- BEN: In base classes. Yeah. Yeah, but then it
comes down to-- there's a few reasons for that. VICTOR: Load times is
the big one that I came across. BEN: Well, I think he's
asking more about interfaces versus base classes because
those are the two options. VICTOR: Oh, yeah,
yeah, I think you're right. BEN: So base
classes are just a little easier to use because
you don't need to expose things in both the interface
and the base class if you have a base class
that make sense to do it in one place. Interfaces are a little
slower like performance-wise in some cases. Also, it's harder to have an
interface that works in both C++ and Blueprints. So if you want to have your base
classes being native of code and used from
Blueprints, base classes are currently easier
than interfaces for a variety of reasons that
are not worth going into. But yeah, I mean,
my general advice is use base classes
when it makes sense. And then when it doesn't
make sense, use interfaces. VICTOR: I
especially like to use interfaces when I have
similar functions, and I don't want different
actors to have the same base class because they
are vastly different but I still need to know
how much health does is have or any of the other
typical results. Being able to return
that data and essentially call a function on a class
that you don't actually have a reference to
is extremely useful, especially during prototype
phases when you're not sure about the entire
hierarchy of all of the classes that you'll actually be
shipping with the game. BEN: Yeah, totally. That's kind of the
big thing, right? Basically every game should
have like a base character class that's game specific. Like, I think pretty much
every game ends up doing this. So that's a pretty natural
place to put some of those like general functions. But yeah, I doesn't really
make any sense to have like a-- vehicles and characters can't
be the same base class really. So stuff like that, interfaces
make way more sense for those. VICTOR: In the end, we
always talk about performance and ways to manage it. But one of the most
important things is that you can work
with it as a human and understand what's going on. And so occasionally you can pay
a little bit of the performance cost and take some of the
manageability of the system over that performance cost. But always run your
profilers to make sure that it's the right choice. That was our last question. I think we went
through all of them. Yeah, another one about
the Google plugin, which unfortunately,
we're not aware. Maybe we can provide some
information on that later. With that said, Ben,
is there anything else you want to leave chat with
before we sign off here today? BEN: Yeah, I
think so, a few things. So I was thinking,
I think I probably want to write up some of
this in an informal form. I might throw this on the UE4
community wiki at some point. So people who don't
like to read video, you know, people who don't want
to watch a whole video to get some of the details out of it. So I'm probably
going to write up something in the next
few days, put it there. I'll link that in the
forum thread if I do that. VICTOR: Yeah, I'll
put it in the OP as well. BEN: In general,
a lot of these systems are designed as kind
of like a toolbox to be used by moderately-- these systems are
mostly designed for like moderately-experienced
programmers. Like if you're like a
super-experience programmer, you've either already
found this or you have your own way of doing this. But if you're like a
brand new programmer, you might not be ready for that. But like it's a good
moderate-experience programmer. So because of that,
it doesn't do a lot of things all the way for you. The system is
designed to do things like 95% of the
way for your game. So think about that when
you look into the systems and are looking at
the headers and stuff. Like it's designed to
be a toolbox more than it is like a
all-in-one solution. I think it's really the
only thing I wanted to say. I just pull my "who am
I" slide I guess, and-- yeah, so you can
poke me-- actually, your poke me at
Twitter if you want. I think that'll work. Or obviously on the
forum thread's great. And I usually think
about this stuff fairly often, so there's
a lot of interesting areas of combining gameplay
and enginey stuff. And like I mentioned,
Unreal Slackers has good discussions
about that sometimes. So those are all different
ways to get in contact with me if you want. I think it's everything I
wanted to say from my side. VICTOR: Awesome,
well, with that said, I do want to let everyone know
that if you thought that it was a lot of content, and you wasn't
really sure where we discuss something and you want
to go back and watch the VOD, within a couple
of days after the video-- we didn't stream to YouTube
today unfortunately. But the VOD will
go up on YouTube and we timestamp all of
the Inside Unreal episodes, so that it makes
it a little easier for you to find the section
that you might be looking for. Within about a week of
the upload on YouTube, we also provide captions
for the entire stream. We also upload a link
to the full transcript with timestamps on it. So that's also another way
that you can go and download the transcript-- Control F and search for
your key terms, and then find the timestamps where we
discuss those specifically. Make sure you follow
us on Twitter and all of our social media accounts. That's where all the great
news will be announced first. As well as our live streams,
future online events that we'll be doing this year,
as well as all of the live streams. An even earlier look at the
live streams coming up you can find in the events
section Unreal Engine forums. If you enjoy our
content, make sure you hit that follow on Twitch. And if you are streaming
Unreal Engine content, there's actually an Unreal
Engine tag that you can use, which makes it a little bit
easier for us and others to find you and your content. I usually combine the
Unreal Engine tag together with the game development tag. And then you can search
for filters using those, which is pretty handy. Next week, we're going to
have Michael Prinke covering the Chuck Downloader,
which is somewhat relevant. Thankfully, he was
nice enough to come on a little bit of last notice. So I'm excited for that. That news might go
out Monday, Tuesday. But he will be on next
Thursday on our regular time-- 2:00 PM, Eastern time. That said, thank you so much Ben
for coming on and talking to us and explaining about
the Asset Manager. And thanks to KaosSpectrum
who reminded me that this was a topic
that we hadn't covered yet on the live stream,
so big shout out. Cool. BEN: I was very glad
to talk to some of the stuff. Yeah, thank you for hosting me. It was a good experience. VICTOR: For sure. If we have any future
updates to the Asset Manager, please let me know, Ben. And then we'll make sure to
bring you back on the stream again. With that said, I hope you all
are staying safe out there. We will see you again
next week, same time. Take care everyone.