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.