Reducing the size of your iOS or Android game (Unreal Engine 4)

Looking to reduce the size of your UE4 iOS or Android app? Here is a handy list of the things I've done to reduce the size of Garden Wars and get under those pesky size limits (this is applicable to UE 4.13).

Measuring the app size

First of all, the way I check out the size is using 3 free programs: 7-zip, UnrealPak and WinDirStat (optional).

  • 7-zip will happily open .ipa & .apk packages, and in there you can see the packed sizes of things.
  • UnrealPak can be used to extract your pak file content and see what got packaged. Make a .bat file with this line in it:

    %~dp0\..\..\UE4\4.11\Engine\Binaries\Win64\UnrealPak.exe %1 -extract %~dp1\extracted

    ...then just make a shortcut to the bat file on your desktop - after that you can drag any pak file onto it, then find the extracted contents right next to your pak file.

  • WinDirStat is a handy filesize visualiser that gives you a colourful view of a folders contents, so you can very quickly see what the biggest users are. Once you have extracted your pak file, you can tell WinDirStat to analyze the extracted pak contents - you will most likely see huge music files, huge font files and some large engine materials.

Reducing the app size

Now here is what I do to reduce size:

  1. In my experience the single biggest win you can get is having a good PakBlacklist.txt in your Project\Build\IOS folder (or Android). Here is mine if it helps. You could probably add a lot more to it if you wanted to be more meticulous and daring. Note that you can save a large amount by removing certain engine fonts as well (LastResort, NanumGothic, DroidSansFallback), but you need them if you want to display Asian/Russian languages.
  2. Make sure in your packaging settings, you are packaging for shipping/distribution, you are using package compression, and you are using a pak file.
  3. Make sure you are compressing your PNGs (splash screens, icons) appropriately.
  4. Music files are huge in the big scheme of things - try to compress the imported sound wavs as heavily as you can (you can make that number pretty small before you notice a difference on a phone speaker), or even reduce the number of music tracks you are using.
  5. Make sure you're not including the armv7s binary on iOS, not really necessary (and I think it defaults to off now anyway).
  6. There are also some specific shader permutations you can disable at the bottom of the rendering section in project settings, to maybe save a little (assuming you're not using them), and probably other settings you could experiment with too.
  7. Go through all plugins and disable all the ones you don't need. e.g. for iOS you don't need the Android media player and vice versa
  8. Remember to do a fixupredirects on your content before shipping, leftover references can add up.
  9. I also deleted all unused maps and assets manually - for some reason, certain materials were getting cooked & packaged even though they were never used/referenced by anything.
  10. I opened up WorldGridMaterial and DefaultMaterial, and replaced their shader inputs with constants to reduce the size of those large materials (I also tried changing them to unlit but it seemed to cause crashes).
  11. Lastly, if you're compiling (i.e. you have the github version of the engine), you can change some build flags to reduce the code size - in your Project\Config\IOS\IOSEngine.ini:
  • WARNING: if you have bCompileICU=false, it appears to break localisation, if you use it
  • I get compile errors if I try to have bCompileRecast=false, so I leave that in
  • I use the UMG widget, so I leave bCompileCEF3 as true
  • I seem to remember either bCompileLeanAndMeanUE=true or bCompileForSize=true actually causing my IPA to be bigger... not sure why. It'd be great it someone from Epic or just someone who knows their way around the engine code could elaborate in more detail as to what those two actually do, and if they have any drawbacks... bCompileForSize sounds like its prioritising size over performance, or something... kinda like bRuntimePhysicsCooking=True

I end up with a "Universal" IPA of ~123MB. Once they're processed by the App Store, the individual sizes for all devices range between 82-89MB, thanks to iOS 9 app thinning, I think (the universal is only large because it has binaries for all phones in it). I'm guessing the universal IPA size doesn't really matter and that most people can download it over the cellular network since the downloads for each device are well under 100MB.

The final ~123MB universal IPA consists of: 82.5MB engine code (this is what gets reduced once you upload) + 12MB engine content + 10MB of my game content + 16.5MB of audio + 2.5MB startup movie / icons / splash screen.

On Android, the APK only comes to about 47MB (without any compiler flag changes).

Overall I'm happy enough with the size I got to. I feel like there is a whole heap of code I could rip out of the engine for this tiny game (if I had lots of time... and knew how to program), and a few more things I could do with my content to reduce size, but it starts to get a bit dodgy and not worth the trouble :)

Dynamic weather/season cycle (UE4)

Today's post is to briefly explain how I implemented the dynamic weather/season changes in Garden Wars, using Unreal Engine 4 (see the last pic for the end result).

To keep things feeling fresh and different during play, the environment will change every couple of minutes. There are four "seasons": Summer, Autumn, Winter and... "Rainy". To achieve this, first of all, the materials used on the environment needed to be set up with colors that I could change to make them look like they belong in the season:

Color parameters for each season set up ready to be changed dynamically

Color parameters for each season set up ready to be changed dynamically


This needed to be done for every material on screen (luckily the environments in the game are very simple).

Then, I needed to set up the environment objects so they could be changed during play. To do this, I made a special subclass of StaticMeshActor that would create itself a dynamic material instance at startup, and it would also store information about what materials/colors to use for each season:

Custom child class of StaticMesh, with special logic for changing colors

Custom child class of StaticMesh, with special logic for changing colors


This custom actor has an array of four materials - one for each season. Also, to make sure I didn't have to specify those materials on every single mesh placed in the environment, I also made several subclasses of my custom actor - one for each type of mesh e.g. a FencePost subclass, a TreeTrunk subclass, etc. I could build the environment with normal static meshes, then all I had to do was select all of each type, and replace them with my custom actor via the right-click menu.

Next, I made a Timeline node in the Game State class. When this is triggered, for every season-changing actor in the level, it gets the colors for the current season and the new season (by looking up the array of materials I mentioned before), and smoothly interpolates between those colors over a few seconds:

Timeline smoothly changes all material instances when the season changes

Timeline smoothly changes all material instances when the season changes


Then, for testing and tweaking purposes, I made a one console command to trigger the season change, and another command to force-update all of the materials for the current season (allowing me to tweak colors of everything at once in-game). After that I went in-game and tweaked the materials for every season until it looked right:

Colors are all tweaked at once in real-time as the game plays

Colors are all tweaked at once in real-time as the game plays


Lastly, I made some particle systems and ambient sounds to compliment the color changes - snowflakes, leaves blowing in the wind, rain, etc:

A particle system blows leaves across the world during autumn

A particle system blows leaves across the world during autumn


Thats it! Here's a timelapse of it during gameplay:

The end result!

The end result!

There is probably more that could be added (like snow accumulating on things, a night and day cycle maybe), but due to time and mobile performance limitations (and the K.I.S.S. principle) I have kept it fairly simple.

There is quite a lot more complexity to the whole system than what I've described above, but hopefully that brief explanation is interesting enough. If you have any questions about specific things feel free to ask!

Don't forget to subscribe or follow in Twitter/Facebook/Reddit for more articles (links are at the top of this page)!