Build Size

Why?

Currently Apple and Google both limit Over-the-Air downloads to 100MB. This means that if you want your users to be able to download your app without a WiFi connection, it needs to adhere to these size limits.

Let’s Begin:

As with many aspects of life, there’s no point rushing into things blindly – although that does sort of help natural selection along.

With build sizes, the same is true. You’ll get much greater effect determining which part of your project is contributing the most to build size and going after that area, rather than just randomly gutting bits here and there, hoping for the best and wasting time.

For instance, you can compress audio all you want, but if you have a 4K texture that’s used on a leaf in the distance, you’d be much better off finding that leaf and changing the texture size to something more along the lines of 32 pixels or even less.

This post is about finding that tricksy little leaf …and a bit more too.

The Size Breakdown:

After doing a build, Unity will put a breakdown of the various asset sizes in the “Editor Log” which you can find by clicking the “Open Editor Log” option in the menu at the top right corner of Unity’s console window (or Google “How to find the Unity Editor Log”).

  1. Open the Editor Log.
  2. Find the Size Breakdown by search around till you find something that looks a bit like this:

Textures 154.5 mb 56.8%
Meshes 8.9 mb 3.3%
Animations 75.5 mb 27.7%
Sounds 18.6 mb 6.8%
Shaders 170.5 kb 0.1%
Other Assets 4.8 mb 1.8%
Levels 537.8 kb 0.2%
Scripts 984.2 kb 0.4%
Included DLLs 5.1 mb 1.9%
File headers 3.1 mb 1.1%
Complete size 272.2 mb 100.0%

Here we can see that for this project: Textures take up the lion’s share of my project’s size, followed by Animations, Sounds and then Meshes.

These are the 4 categories we will be focusing on first, and after, if the file size is still too big, we’ll go through the Resources folder to gut any unused assets before finishing off on the Player Settings.

The reason going through Resources is last, is this is the most likely to cause issues at runtime if you accidentally remove the wrong “unused” asset. But if you are 100% sure that certain assets in the Resources folder aren’t used, take a look at the Resources section of this post first.

Note:

  • Unity re-codes imported assets, so the file format in the Unity project isn’t as important as its compression settings in the Inspector. E.g. A multi-layer Photoshop texture will be flattened and compressed before building.
  • Unity strips out most unused assets during the build (except scripts, which are usually tiny anyway). It won’t however strip assets out of the Resources folder which is why we need to do that ourselves.

Tip:

  • I recommend doing a build after each optimisation phase and giving it a descriptive name so you can see just how much impact each phase has on your build size.

Textures:

Here our main aim is to find that 4K texture applied to a little leaf off in the distance.

Luckily Unity’s Editor Log can help us here too as it lists the textures in the project from largest to smallest:

Textures 154.5 mb 56.8%
5.3 mb 2.0% Assets/Resources/Textures/Ring/old/ring_diffuseColour.png
2.7 mb 1.0% Assets/Resources/Textures/Ring/ring01.1Surface_Color.tif
2.7 mb 1.0% Assets/Resources/Textures/Minions/Old textures/Bomb_Diffuse1.jpg
2.7 mb 1.0% Assets/Resources/Textures/Minions/Old textures /Bomb_Diffuse2.jpg
2.7 mb 1.0% Assets/Resources/Textures/Player/Shorts Skins/Yellow_Diffuse_003.jpg
etc…

Based on this we can decide which textures to tackle first for greatest effect.

The parameters we will be looking at changing are:

  • Size

    • Each time you go down a power of 2 in your images’s dimensions, you are decreasing the amount of pixels by 3 which drastically decreases the file size.
    • This is why it is a good idea to set the image size as small as possible within Unity without too much loss of quality.
    • This will likely involve a lot of trial and error but eventually you should start to recognise what sizes the various images in your project should be.
  • Texture Format

    • Start with the lowest quality setting and see how high you need to go to achieve something that looks acceptable:
      1. Compressed:

        • Most common for diffuse textures.
      2. 16 bit:

        • Lower quality True Colour.
      3. True Colour:

        • Highest Quality.
    • Bare in mind that your textures most likely need to look good across multiple devices with varying screen resolutions.
    • Take a look at the Unity Manual, specifically the Texture Format section for more detail.
  • Mip Mapping

    • Mip maps are smaller versions of your texture that are automatically created if this option is turned on.
    • They may almost double your texture size but are used to optimise graphics performance so it is a good idea to leave this option on for objects that will be moving around your scene, like textures used on characters and scenery (if you have a moving camera) etc.
      • Test performance on lower end devices if you do end up turning Mip Mapping off on any textures.
    • They can and should be turned off for UI.
    • For more information, take a look at the Mip Maps section of the Unity documentation.

Animations:

In the Animations Tab, the option we’re interested in is:

  • Anim. Compression:

    1. Optimal:

      • Only available for generic and humanoid rigs.
      • Reduces keyframes and increases performance.
    2. Keyframe Reduction and Compression:

      • Reduces the amount of keyframes and also compresses stored animation files resulting in smaller build size.
      • If animations are producing inaccuracies, lower the “Animation Compression Error” values.
    3. Keyframe Reduction:

      • Reduces the keyframes on import, resulting in both performance and build size optimisations.
    4. Off:

      • Not recommended. Even for high precision animations it is recommended to use Keyframe Reduction and just lower the Animation Compression Error values.

Sounds:

Most sound clips will be relatively small so unless you have tons of effects, there probably won’t be much effect targeting sounds other than the longer, music tracks.

There are quite a few options here for different cases so I’ll go through the important ones:

  • Force To Mono:

    • Most sounds have a separate waveform for the Left and Right speakers.
      • For mobile, unless your users are going to be using headphones, this is often barely audible so sounds can be set to Force To Mono which should reduce the file size by half.
  • Compression Format:

    • ADPCM:

      • For short sounds like footsteps and impacts. The file size will be larger than Vorbis/MP3 but that doesn’t usually make a massive impact as far as the file sizes go.
    • Vorbis/MP3:

      • Best for medium length sound effects and music.
  • Sample Rate Setting:

    • There is no point increasing a sample rate higher than what the source file has, so check the source rate before overriding it.
    • You can also see the effect your compression settings will have on the selected audio clip in the inspector which is quite handy.
    • Override Sample Rate:

      • 22,050 Hz: For short sounds like footsteps and impacts.
      • 44,100 Hz: For longer clips and music. Any sound with a lot of detail, like a song multiple instruments.

Another thing you can do to decrease the size of large audio files is to make the loops shorter. Open them up in your favourite audio program and start chopping. Think about how long you expect users to stay in any one menu or level and shorten the audio clip so that it loops a few times without feeling too repetitive. If you cut down the a 10 minute song to a 1 minute loop, you’ve most likely saved yourself about 5 MB worth of space.

Meshes:

In the Model Tab, the only option to concern ourselves with is:

  • Mesh Compression:

    • The higher the compression, the smaller the file size will be, though as with textures, there is a tradeoff in quality.
    • Increase the compression as high as possible without seeing too much of a quality loss.

Note:

  • A compressed mesh has less vertices, meaning less calculations are needed to be done at runtime so this would also optimise graphical performance.
  • The “Optimise Mesh” option is for graphical performance and is a good idea to leave on too. More information can be found here with more technical detail here.

Resources:

All assets in the Assets/Resources are included in the build. This is so that they can be loaded dynamically at runtime, using Resources.Load(…);

This is a really hand feature of Unity, though sometimes people (usually of the Artistic persuasion) forget to remove older, unused versions of assets.

For example, in the project I’m busy optimising, there’s this little guy:

Assets/Resources/Textures/Ring/old/ring_diffuseColour.png

To be safe, rather than deleting the seemingly unused assets, we can just create another folder in the project called something along the lines of “Unused“.

Remember: Unity will strip unused assets unless they are in the resources folder, so by moving these assets out of the Resources folder, they won’t get included in the build.

But how can I make sure the asset isn’t being loaded using “Resources.Load(…)”?
Well unfortunately, the only way to be 100% sure is to look at the code.

The steps are as follows:

  1. Copy all the assets from “Resources” into the “Unused” folder.
  2. Do a search in the code and look for all occurrences of “Resourced.Load”.
  3. Systematically copy each resource that is referenced back from the “Unused” folder into the “Resources” folder, making sure the folder structure remains the same.

E.g: After copying out all the assets, I’ve done a search and found the following line in code:

Resources.Load(“Main game/Boss”)

The asset was moved into the unused folder, it’s path being:

Assets/Unused/Main game/Boss.prefab

 

 

Which needs to be copied to:

Assets/Resources/Main game/Boss.prefab

Note: Most of process could be automated and you’ll probably want to do so for larger projects.

This is fine for smaller, simple projects but when it comes to larger or more complex ones there are a few things to watch out for:

  • Variables:
    • Resources.Load(_bannerLocation)

    • You’ll need to find out what value is being stored inside the variable “_bannerLocation”.
      • Where is it assigned?
      • Does the value ever change and if so, what does it change to because that asset will need to be included too?
  • Dynamically constructed paths:
    • for(int i = 1; i < 4; i++)
      {
      Resources.Load(“Main Game/Textures/Enemy_” + i);
      }

    • The code above creates the following 3 paths:
      • Main Game/Textures/Enemy_1
        Main Game/Textures/Enemy_2
        Main Game/Textures/Enemy_3

      • So all of those assets will need to be added back to the Resources folder.

For this reason, it’s a lot easier to just make sure your team knows to remove unused assets from the Resources folder before you get to this point…unfortunately this knowledge is far too often only obtained when it is too late.

If you accidentally remove an asset your game does actually still load from the resources folder, it will throw a NullReferenceException. Be on the lookout for these after removing the unused assets from your game.

Player Settings:

  • Api Compatibility Level:

    • .Net 2.0 Subset:

      • We don’t usually need all the functionality of the entire .Net library so we can generally get away with using this trimmed down version.
  • Stripping Level:

    1. Use micro mscorlib:

      • Smaller version of mscorlib.
      • Try using this.
      • If it doesn’t work, go up the options till you get to an option that does.
    2. Strip Assemblies:

      • Unused classes and methods are removed from the DLLs.
    3. Note: 
      • You can tell Unity not to strip certain classes by creating a stripping blacklist.
      • More information can be found here.
  • Script Call Optimisation:

    • Set to Fast but No Exceptions:
      • This stops Unity from generating extra error handling code.
      • Not recommended during development, as it makes debugging considerably harder. Though it is fine for release if you really need to squeeze every last drop out of your build size.
  • Optimize Mesh Data:

    • Removes additional information on the meshes that isn’t required (by their textures).

More information on the Build Player Settings.

Conclusion:

So, my project’s build sizes started at 131MB and at each stage decreased to:

  1. 95MB – removing unused assets in Resources.
  2. 70 MB – Textures.
  3. 63 MB – Animations.
  4. 52 MB – Sounds.
  5. 50 MB – Meshes.
  6. 49 MB – Player Settings.

This will be different with every project, but hopefully you get a rough idea how much a badly (or just not at all) optimised project can be brought down regarding build size.

As with most things in life, this is all a lot easier if you keep note of the app size and apply the various size optimisation techniques during development.

If you can’t get your app below 100MB for Google, you can take a look at splitting the application binary which makes your .apk considerably smaller by moving a large portion of your game’s assets into an .obb file which can be downloaded later.

 

 

Sources:

Advertisements