On little (Unreal) things and file sizes

unreal engine, optimization, and game dev
a movie still from The Lord of the Rings - The Fellowship of the Ring, 2001
The Lord of the Rings - The Fellowship of the Ring, 2001

(Boromir speaking) It is a strange fate that we should suffer so much fear and doubt over so small a thing. Such a little thing.

TL;DR

Side-note: Want to know more about creating a CI pipeline for your Unreal Engine project? Read my previous post!

Context/Lore (Press Y to skip)

Videogames are getting way too big… Literally! – A concerned gamer

The previous sentence has a horrifying amount of subtext:

It’s a troll bait in gaming communities. And it’s not the purpose of this post. I want to look at a tiny slice of that sentence: Games being big as in their file and download size.

Again, this could open a whole Pandora’s box of discussion points, due to the subtext:

But… it’s not the purpose of this post.

I want to take it at a “Sub-atomic/Molecular level”. I’ll get to the point:

All this late talk about game file size made me curious:

So I went ahead, grabbed a 3 year old stale project I did when studying Unreal Engine 4, surely it was just a few MB (megaBytes) in file size, right? Hold up…

The Problem

Suddenly there was a problem where previously there was none. How does a bland super tiny game take up 600MB in file size?

For millennial context, that’s almost the entire size of a CD, and it’s half the file size of Valheim with 0.000001% of that game’s value.

The game itself, if we can call it a full game:

Here’s a video of what I’m talking about: https://youtu.be/_PE0yH3BuSE

At the end of the day, it was just an experiment for learning. Not a proper shippable game, but its size of 600 MB left me curious enough to go on a small expedition.

Side-note: random bit of context, just the development environment for both Unreal Engine 4 and 5 takes up about 150 GB of space… scream disappointed

Investigation/Solutions

Side-note: the repository used: https://github.com/filfreire/SimpleFPSTemplate_filfreire

After some research I took note of a few possible suspects:

We’ll consider the baseline:

Removing unused plugins

First we’re going to disable all plugins which we don’t use. Any plugins for different IDEs that we don’t use or other platforms like Android, iOS and others can potentially be removed. My project is also not a VR project, so pretty much all of that can be scrapped.

These were some of the plugins I initially disabled:

 AndroidDeviceProfileSelector, AndroidMedia, AndroidMoviePlayer, OnlineSubsystemGooglePlay, AndroidPermission, GooglePAD, OpenXRHandTracking, MagicLeapMedia, MagicLeap, MagicLeapPassableWorld, LocationServicesBPLibrary, ExampleDeviceProfileSelector, IOSDeviceProfileSelector, LinuxDeviceProfileSelector, LuminPlatformFeatures, MLSDK, WmfMedia, WebMMoviePlayer, UdpMessaging, MobileLauncherProfileWizard, MacGraphicsSwitching, MobilePatchingUtils, AppleMoviePlayer, WindowsMoviePlayer, GoogleCloudMessaging, OnlineSubsystemNull, OnlineSubsystemUtils, OnlineSubsystemIOS, OnlineSubsystem, CLionSourceCodeAccess, CodeLiteSourceCodeAccess, KDevelopSourceCodeAccess, NullSourceCodeAccess, RiderSourceCodeAccess, XCodeSourceCodeAccess, PerforceSourceControl, PlasticSourceControl, SubversionSourceControl, OculusVR, OpenXR, OpenXREyeTracker, SteamVR

The final size after removing these plugins: compressed was 260MB, uncompressed 562MB.

That’s about 5% reduction in size. Not a massive change, but I suspect also not representative for most Unreal-based games. As comparison, take Hell Let Loose, a multiplayer tactical FPS. According to the game files, it currently uses Unreal Engine 4.25 and takes up about 38.9 GB. Reducing that game by 30 MB of plugins will still make it a 38.9 GB game, hence the reduction is a bit irrelevant/trivial 😅

Remove unused content

My next target was looking at potential unused content in the game. Some folks in the community (here’s one example, here’s another example) point out to this issue. Usually the recommendation is to look out for materials, say, textures, models and even entire levels that are unused in our final shipped game.

First, I looked into the size map of the content I had in the game:

Size map of my project, taking up 67.5 MB
Size map of my project, taking up 67.5 MB

The game specific content taking up about 67.5 MB meant that the bulk of the size was coming from another place. I deduced it would likely be Engine related. So I checked the size map of the Engine:

Size map of the Engine content, taking up what seems to be 229.6 MB
Size map of the Engine content, taking up what seems to be 229.6 MB

At first I thought this Engine specific content was impacting the final game size. I happily started deleting stuff. But upon closer inspection I was deleting stuff directly from my Unreal Engine install path. I had to repair my Unreal Engine installation afterwards and re-download what I had deleted 😅

It had no impact on file size.

There were a still few materials that caught my attention in the contents of my game. For example, I had a simple cube taking up about 11MB:

Example of a simple material taking up 11MB
Example of a simple material taking up 11MB

So I attempted to remove those. By removing materials like that the size of final uncompressed game was… *drumroll*… reduced by roughly 2 MB2MB?!

Epic fail.

Maybe I didn’t do it right. So I tried a different approach that some folks recommend of migrating assets into a new completely empty project.

Migrating assets in Unreal
Migrating assets in Unreal

If the project is just made up of Blueprints, then the migration is straightforward. In my case the project also had a handful of C++ source code, making the migration difficult.

This was a point also where the whole thing started to look like a rabbit hole. At the time of writing:

The sizes weren’t improving and it felt like a giant rabbit hole to get lost into. Since I wasn’t getting anywhere at the time, I skipped to the next reduction attempt.

Side-note: My untested hypothesis on the size difference of my project against blank projects is that the Engine version we start a project with affects the final size of the game later on. Even after we do successive conversions to more modern versions, the version we base our game initially will perhaps be missing stuff? I’m not sure. I was using Tom Looman’s SimpleFPSTemplate as a starting point, which was first released on Unreal Engine 4.17 and successively upgraded. Perhaps that had an influence in something, but it would require a handful of successive experimentation with different engine versions to get to the bottom of.

Setup Non-debug/Shipping build configs

One of the things that I overlooked when I wrapped this game prototype was the fact that the builds by default were still development builds. It was possible to open the developer console inside the game and see a lot of different debug information.

Unreal has a setting for Build configuration when Packaging that can be set to avoid including this development tooling in the final game.

I went ahead and attempted the suggested Shipping configuration as opposed to the Development one, and it helped a lot the file size reduction.

It nearly halved the size of my game prototype, going from 595 MB to bellow 250 MB when uncompressed. On top of that, removing both unused plugins and materials helped drop the size to 220 MB: a whooping 63% difference!

To set up this setting in an pre-existing CI pipeline is easy. Take an example batch script that packages the game:

set ueLocation=%~1
set projectLocation=%~2
set projectName=%~3
set target=%~4
set packageFolder=%~5

"%ueLocation%\Engine\Build\BatchFiles\RunUAT.bat" BuildCookRun -project="%projectLocation%\%projectName%" -nop4 -utf8output -nocompileeditor -skipbuildeditor -cook -project="%projectLocation%\%projectName%" -target=%target% -platform=Win64 -installed -stage -archive -package -build -pak -iostore -compressed -prereqs -archivedirectory="%projectLocation%\%packageFolder%" -clientconfig=Development -nocompile -nocompileuat

We only need to change the -clientconfig=Development into -clientconfig=Shipping plus add the -nodebuginfo argument, and the Unreal Automation Tool does the rest.

Results for Unreal 4.27

Before going to Unreal Engine 5 comparisons, let’s do a catch-up. Here’s a table with all the results I had achieved up until this point:

Engine version Action Compressed (MB) Uncompressed (MB) Difference vs default compressed (%) Difference vs default uncompressed(%) Compression reduction
4.27 base default 275 595 n/a n/a -53,8
4.27 rm plugins 260 562 -5,5 -5,5 -53,7
4.27 rm plugins + rm materials a bit 260 560 -5,5 -5,9 -53,6
4.27 shipping version 162 228 -41,1 -61,7 -28,9
4.27 shipping version + remove more plugins 158 220 -42,5 -63,0 -28,2

The key takeaways until now:

And this is all for the exact same engine version (Unreal Engine 4.27).

How would we fare with the latest version?

Upgrading from Unreal 4 to 5

I was curious to see how the latest version of the Engine would impact sizes. I converted my 4.27 project for the 5.2 version of the Unreal Engine.

The compressed size was 308 MB, and uncompressed was 687 MB. Just by switching to a recent Engine version, there’s a 15% increase in uncompressed size.

I kept the upgrade to version 5.2 on a separate Git branch and attempted to run through the same steps I ran above for version 4.27, here’s a table with the results:

Engine version Action Compressed (MB) Uncompressed (MB) Difference vs default compressed (%) Difference vs default uncompressed(%) Compression reduction Difference vs UE4 default(%)
5 default 308 687 n/a n/a -55,2 15,5
5 rm plugins + rm materials a bit 297 669 -3,6 -2,6 -55,6 19,5
5 shipping version 178 267 -42,2 -61,1 -33,3 17,1
5 shipping version + remove some more plugins 171 238 -44,5 -65,4 -28,2 4,4

In all reduction cases the 5.2 version always took up more space than the 4.27 version. The unused content removal didn’t impact the size in a meaningful way so I left it out of the table.

Similarly to Unreal Engine 4, in the case of my game there’s a difference of around 60% size reduction by removing plugins and setting up the “Shipping” build configuration when packaging the game as opposed to the development one.

There is an extra key takeaway. If we look towards the last experiment shipping version + remove some more plugins, we’ll see that the final uncompressed size is not that much bigger than Unreal Engine 4. To understand the possible cause of this, we have to look at what was done in each:

On Unreal Engine 4.27 the final list of deactivated plugins was smaller than the one for Unreal Engine 5.2. This could probably be tuned further and revisited, but I was content with the results up to this point.

Side-note: the list of deactivated plugins for version 4.27 is found here and for version 5.2 it’s here.

Wrapping-up

There’s a few considerations to keep in mind:

As a baseline for reducing file size “any” Unreal Engine project, my the game prototype experiment is not an adequate sample. File size optimizations are not necessarily proportional to the game size.

I found comments on forums saying that Plugins that are enabled on the Unreal Editor but not mentioned in the .uproject file shouldn’t impact size of the final packaged build of the game. What I’ve noticed is the opposite: for every attempt at disabling plugins I got some file size reduction out of the final game build.

My assumption on Unreal plugins, that they act as “plug in” extensions, but are not actually needed was incorrect: Disabling some plugins caused things to stop working. For example, disabling some AI related plugins would make my AI Dwarfs stop working and fail to detect the player.

There’s a lasting vibe I’m left with after this exploration. It was cumbersome to optimize the size of this tiny Unreal Engine game prototype. Just to give the reader an idea of frustrations I felt:

Lastly, all this beanie-babies “gold” rush of “AI” helping developers… I see plenty of examples of large language models assisting with bland/shallow content creation, even in the context of Unreal Engine games… but I can’t find anything intelligent out there in the lines of:

Hey we’ve noticed you are not using this 200 MB texture anywhere on your game, but it’s being included in the final build… would you like to unlink/remove it?

This would be more useful for empowering game developers than just regurgitating text that was stolen parsed from crawling the web.

Side-note: This small investigation into the file size issue made me wonder… what would the size of the “same” game project be for the old Unreal Engine 3/UDK nowadays? I’d like to revisit this, even if it’s just for fun.

Future ideas

There’s a chance for a bigger creative exercise out the file size problem. Something like what folks do in the demoscene but applied to Unreal Engine 4/5 in terms of games with the smallest file size possible. That would entail a lot of creative solutions:

Something to consider for future posts. Next up, I want to look more in depth into the Unreal Test framework than I have in my past post.

There’s some bugs that could use some fixing and would be great to try some automated checks. I found a cool one while revising this game prototype, where I managed to both win and lose the game, and trigger both the Mission Complete and Game Over final messages at the same time 😅

Stay tuned for more!

Addendum

Update, July 31st 2023, Removing Prerequisites from packaged builds

A slight reduction is futher possible by disabling prerequisites installers when packaging the game.

By default Unreal adds to the final packaged builds some dependencies installers, like Visual Studio redistributables. For publishing a game out into the world these are for sure needed, but if it’s just a matter of sharing between developers that already have the same dependencies installed, it becomes a tiny size optimization in the builds used for testing.

By removing the -prereqs argument when packaging the game with RunUAT.bat, the following results were achieved:


If you read this far, thank you. Feel free to reach out to me with comments, ideas, grammar errors, and suggestions via any of my social media. Until next time, stay safe, take care! If you are up for it, you can also buy me a coffee ☕