Reducing Unity game file size

posted in Once a Bird
Published December 11, 2014
Advertisement

Hello,

I've been busy porting Twitchy Thrones to Unity. The game was originally programmed in Objective-C, using XCode and the Cocos2D framework. Cocos2D is a great framework but if you want to publish your game for iPhone, Android and PC you have to use C++ and Cocos2D-X. Setting up Cocos2D-X isn't trivial and using C++, of course, implies losing comforts like automated memory management.



As for Objective-C, it's almost exclusively used by Apple and thus can't be used, as far as I know, to port a game to other platforms. This is actually a Good Thing, since it is a monolithic programming language, full of unnecessarily verbose syntax and lacking modern programming language features such as generics, real encapsulation, string operators, etc.

So I turned to Unity and C# to publish Twitchy Thrones on Android and PC. I'm finding Unity amazing, except for a few quirks. If I knew before what I know now I would have started the project in Unity to begin with.

Everything was going well, except... When the Objective-C/Cocos2D version was originally published to the App Store it occupied 17.3 MB on my iPhone. After most features were implemented in the Unity version I installed it on my iPhone expecting a similar file size. Nope. 156 MB! In these days where most games are free or freemium, a big file size can change someone's mind when it comes to downloading your game.

What follows is information I found scattered across the web to solve the problem of Unity's output file size, which I hope will be more useful gathered in one place. smile.png

As mentioned in Unity's own article about reducing file size (http://docs.unity3d.com/Manual/ReducingFilesize.html), the editor log provides a summary of the size of each of your assets after you perform a build of your project. This is what I got for mine:


[quote]
[font=arial]

Textures 65.2 mb

[/font][font=arial]

79.6%

[/font]
[font=arial]

Meshes 0.0 kb 0.0%

[/font]
[font=arial]

Animations 113.1 kb 0.1%

[/font]
[font=arial]

Sounds 10.2 mb 12.5%

[/font]
[font=arial]

Shaders 42.9 kb 0.1%

[/font]
[font=arial]

Other Assets 543.3 kb 0.6%

[/font]
[font=arial]

Levels 92.8 kb 0.1%

[/font]
[font=arial]

Scripts 439.5 kb 0.5%

[/font]
[font=arial]

Included DLLs 5.2 mb 6.4%

[/font]
[font=arial]

File headers 99.8 kb 0.1%

[/font]
[font=arial]

Complete size 81.9 mb 100.0%

[/font]

[font=arial]

Used Assets, sorted by uncompressed size:

[/font]
[font=arial]

6.1 mb 7.5% Assets/Sound/Songs/ThemeSong.mp3

[/font]
[font=arial]

2.7 mb 3.3% Assets/Menus/WorldMap/WorldMapBirdFlockMiddle.png

[/font]
[font=arial]

2.7 mb 3.3% Assets/Menus/WorldMap/WorldMapBirdFlockBottom.png

[/font]
[font=arial]

2.7 mb 3.3% Assets/Menus/WorldMap/WorldMapBirdFlockTop.png

[/font]
[font=arial]

2.1 mb 2.5% Assets/Maps/Map16/HelpPanel6.png

[/font]
[font=arial]

2.0 mb 2.4% Assets/Fonts/Resources/Fonts.png

[/font]
[font=arial]

1.3 mb 1.5% Assets/Menus/Main/BannerFinalChallenge.png

[/font]
[font=arial]

1.3 mb 1.5% Assets/Menus/Main/BannerFinal.png

[/font]
[font=arial]

1.0 mb 1.3% Assets/Maps/Map16/Map16Background.png

[/font]
[font=arial]

1.0 mb 1.2% Assets/Menus/WorldMap/WorldMapClouds2.png

[/font]
[font=arial]

1.0 mb 1.2% Assets/Fonts/Resources/MenuFonts.png

[/font]
[font=arial]

946.8 kb 1.1% Assets/Menus/Settings/SettingsFinal.png

[/font]
[font=arial]

946.8 kb 1.1% Assets/Cutscenes/Cutscene2/Cutscene2Banner.png

[/font]
[font=arial]

946.8 kb 1.1% Assets/Cutscenes/Cutscene1/Cutscene1Banner2.png

[/font]
[font=arial]

946.8 kb 1.1% Assets/Cutscenes/Cutscene1/Cutscene1Banner1.png

[/font]
[font=arial]

946.8 kb 1.1% Assets/Cutscenes/Cutscene2/Cutscene2Cup.png

[/font]
[font=arial]

768.3 kb 0.9% Assets/Menus/WorldMap/WorldMapClouds3.png

[/font]
[font=arial]

768.3 kb 0.9% Assets/Menus/WorldMap/WorldMapClouds1.png

[/font]
[font=arial]

710.3 kb 0.8% Assets/Menus/WorldMap/WorldMapBackgroundLand3_2.png

[/font]
[font=arial]

710.3 kb 0.8% Assets/Menus/WorldMap/WorldMapBackgroundLand3_1.png

[/font]
[font=arial]

710.3 kb 0.8% Assets/Menus/WorldMap/WorldMapBackgroundLand3_0.png

[/font][/quote]

[font=arial]Except for the theme song, all my biggest assets were textures. The problem is, while XCode uses the compressed .png images and even compresses them further with pngcrush (https://developer.apple.com/library/ios/qa/qa1795/_index.html), Unity uses the raw texture data, resulting in enormous file sizes. [/font]

[font=arial]In Twitchy Thrones' case, a 2D pixel art game, reducing the texture quality wasn't an option since any blurry texture stood out from the rest of the game.[/font]

[font=arial]The first solution I came across was bypassing Unity's content pipeline and loading the textures from the .png files in real time. If you change an asset's extension to .bytes, Unity treats it as binary data (http://docs.unity3d.com/Manual/class-TextAsset.html) and doesn't unpack it, thus the asset won't bloat your file size. Here's Unity's code sample from loading a texture from a .bytes file:[/font]

//Load texture from diskTextAsset bindata= Resources.Load("Texture") as TextAsset;Texture2D tex = new Texture2D(1,1);tex.LoadImage(bindata.bytes);
A script component was added to each GameObject containing a SpriteRenderer using the offending giant texture and on the Awake() function the texture was loaded using code very similar to the above. A sprite was then created with the texture and this sprite was set to the GameObject's SpriteRenderer. Problem solved, I thought. But when I booted the game on iPhone, a screen that took less than 1 sec to load before now took more than 10 secs! The performance of Texture.LoadImage is simply terrible.

Luckily, someone with the same problem lead to the solution, the WWW api (http://answers.unity3d.com/questions/511268/texture2dloadimage-too-slow-for-use-ios.html). The game ran perfectly on Unity's editor, but when running on iOS I got an "unsupported URL" error.

Back to Google. Most threads referring the same error warned that the URL is case sensitive and spaces need to be replaced with "%20" as with any URL, but that was not the problem. My problem was, Unity packs the assets in its own format and doesn't keep the original file structure, so the file I was trying to load in the URL didn't exist in iOS. Fortunately Unity provides a way to keep the original assets in their original form in the final build, the "StreamingAssets" folder (http://docs.unity3d.com/ScriptReference/Application-streamingAssetsPath.html).

Note that unlike "Resources" folders, which can be placed anywhere in the hierarchy of the "Assets" folder, the "StreamingAssets" folder must be placed in the root of the "Assets" folder. After placing the textures in the new folder, with their extension changed to .bytes for the reason mentioned above, I changed my GameObjects and respective scripts.

Here's one of the final GameObjects (The Sprite should be set to "None" in the SpriteRenderer):

Unity1.png

And a link to the SpriteTextureLoader script (Sorry for not putting it here but the formatting got messed up when I tried):

http://www.onceabird.com/tt/code.txt

A couple of notes:

  • The main downside of this method is that the sprites won't be visible in the Editor when the game's not running. But then again in most cases this method should only be implemented when the game is nearly done, as premature optimization is the root of all evil.
  • Although I haven't experimented, this method should work with other asset types, such as textures for models in a 3D game.
  • I chose to create a script component for each GameObject where needed, but obviously this code can be used to load all textures in a loading screen if needed.

I only changed the biggest textures so far, but right now Twitchy Thrones occupies 87 MB on my iPhone, which is 55% of the original size.

EDIT: Fixed linked SpriteTextureLoader script to work on Android.
11 likes 17 comments

Comments

riuthamus

Very nice read, thanks for the time and effort put forth on this.

December 12, 2014 05:33 AM
Liort

We also have a component that does the texture loading, and we have implemented an editor helper script that automatically replaces a sprite renderer with the new system, as needed.

Whenever we find a sprite that is too large, we can right-click it and automatically switch to the .bytes solution.

This is done by adding code similar to this:

/// <summary>
/// Creates a .bytes object equivalent from a Sprite.
/// </summary>
[MenuItem("CONTEXT/SpriteRenderer/Create .bytes texture")]
private static void CreateBytes(MenuCommand command)
{
var spriteRenderer = command.context as SpriteRenderer;
var sprite = spriteRenderer.sprite;
if (sprite != null)
{
// Copy sprite to .bytes (or rename)
// Add a component to this game object
}
}
December 12, 2014 10:06 PM
Demosthenes

We also have a component that does the texture loading, and we have implemented an editor helper script that automatically replaces a sprite renderer with the new system, as needed.

Whenever we find a sprite that is too large, we can right-click it and automatically switch to the .bytes solution.

This is done by adding code similar to this:

/// <summary>
/// Creates a .bytes object equivalent from a Sprite.
/// </summary>
[MenuItem("CONTEXT/SpriteRenderer/Create .bytes texture")]
private static void CreateBytes(MenuCommand command)
{
var spriteRenderer = command.context as SpriteRenderer;
var sprite = spriteRenderer.sprite;
if (sprite != null)
{
// Copy sprite to .bytes (or rename)
// Add a component to this game object
}
}

Cool! Is it possible to move the file automatically to the StreamingAssets folder with such a script too?

December 12, 2014 11:00 PM
Eck

Very nice. Thanks for taking the time to share this information.

December 13, 2014 05:02 AM
jbadams
Very cool, thanks for sharing! This would probably make a great article if you were interested in [url=http://www.gamedev.net/page/resources/_/gdnethelp/how-to-publish-on-gamedevnet-r2927]submitting[/url] there. :)
December 14, 2014 03:02 AM
mikiex

Have you looked into the difference in runtime memory when running using png assets vs 32bit? When I last looked into using pngs I am sure there was talk of it ending up using more memory when running. For you this probably wouldn't be an issue and download size is more important. With a more memory hungry game it could be an issue though.

December 14, 2014 03:36 PM
_Slin_

Whats up with "Complete size 81.9 mb 100.0% " and the app being 156MB?

Also did you look into compressed texture formats supported by the graphics hardware, like pvrtc for iOS devices (isn´t there a standard in opengl es by now!?)? They might be bigger than some png files and provide lower quality, but if you chose a higher resolution, filesize and quality should be quite good and Unitys Asset pipeline should support them.

But sure, if your textures are mostly low frequency, PNG is a better choice, at least for download size...

December 14, 2014 04:19 PM
Demosthenes

Have you looked into the difference in runtime memory when running using png assets vs 32bit? When I last looked into using pngs I am sure there was talk of it ending up using more memory when running. For you this probably wouldn't be an issue and download size is more important. With a more memory hungry game it could be an issue though.

Great point. I didn't look it up, but there probably is a difference that must be balanced with your pretended app size.

Whats up with "Complete size 81.9 mb 100.0% " and the app being 156MB?

Also did you look into compressed texture formats supported by the graphics hardware, like pvrtc for iOS devices (isn´t there a standard in opengl es by now!?)? They might be bigger than some png files and provide lower quality, but if you chose a higher resolution, filesize and quality should be quite good and Unitys Asset pipeline should support them.

But sure, if your textures are mostly low frequency, PNG is a better choice, at least for download size...

The size of the app in iPhone isn't a direct correspondence with the size reported by Unity. For example, right now my app occupies now more than 80 megs in my iPhone and Unity reports 21.9 megs:


Textures      12.2 mb	 56.0% 
Meshes        0.0 kb	 0.0% 
Animations    115.1 kb	 0.5% 
Sounds        3.1 mb	 14.1% 
Shaders       42.9 kb	 0.2% 
Other Assets  532.5 kb	 2.4% 
Levels        102.2 kb	 0.5% 
Scripts       442.6 kb	 2.0% 
Included DLLs 5.2 mb	 23.8% 
File headers  115.7 kb	 0.5% 
Complete size 21.9 mb	 100.0% 

Used Assets, sorted by uncompressed size:
 2.0 mb	 9.1% Assets/Fonts/Resources/Fonts.png
 1.5 mb	 7.0% Assets/Sound/Songs/ThemeSong.mp3
 1.3 mb	 6.1% Assets/Fonts/Resources/CutsceneFonts.png
 1.0 mb	 4.6% Assets/Fonts/Resources/MenuFonts.png
(...) 

As for using PVRTC, it is an alternative as mentioned in the first link of the original post, but in a pixel-art game such as the one mentioned any blurriness would be jarring... And for this and many other games it's more convenient to use the assets as produced initially. Also, PVRTC requires width == height.

December 14, 2014 04:48 PM
Demosthenes

Very cool, thanks for sharing! This would probably make a great article if you were interested in submitting there. smile.png

Thanks! I will.

December 14, 2014 04:52 PM
_Slin_

The size of the app in iPhone isn't a direct correspondence with the size reported by Unity. For example, right now my app occupies now more than 80 megs in my iPhone and Unity reports 21.9 megs:

That is nearly four times the size reported by Unity, so where does all that additional data come from? I am just wondering because 60mb for just additional stuff seems a lot. Thats like if I forget to strip debug symbols and wonder why a library is 100mb instead of 1mb...

December 15, 2014 03:44 PM
Demosthenes

The size of the app in iPhone isn't a direct correspondence with the size reported by Unity. For example, right now my app occupies now more than 80 megs in my iPhone and Unity reports 21.9 megs:

That is nearly four times the size reported by Unity, so where does all that additional data come from? I am just wondering because 60mb for just additional stuff seems a lot. Thats like if I forget to strip debug symbols and wonder why a library is 100mb instead of 1mb...

I don't think it's additional data, just Unity using uncompressed versions of assets on runtime... like mikiex said, it must make a difference in runtime memory usage when you employ compressed assets.

December 15, 2014 05:27 PM
_Slin_

Ahh, I thought the 80mb are the download size. For runtime memory consumption that seems okay. The advantage of a format like pvrtc is that the GPU can actually handle them so they just take the same size in vram as they do on the hard disk and don´t need additional time for decompression. But that would only become in issue if you needed more than 200mb of vram. PNG just gets decompressed and is then used as uncompressed image data, which is often just fine.

December 15, 2014 05:44 PM
alh420

If you use OSX, a tip for reducing the file size even further is to use ImageOptim.

It's a free tool for lossless compression of png-files, which work very well!

We use it for all our .png files, and it reduces the size a lot, without any visual degradation.

I've had files (with lots of transparency) drop to a quarter of the size.

December 16, 2014 01:39 PM
Demosthenes

If you use OSX, a tip for reducing the file size even further is to use ImageOptim.

It's a free tool for lossless compression of png-files, which work very well!

We use it for all our .png files, and it reduces the size a lot, without any visual degradation.

I've had files (with lots of transparency) drop to a quarter of the size.

Sounds great, I'll give it a try.

December 16, 2014 02:39 PM
lask1

Thanks! Ill be looking into doing this for my game I am about to release! Very very useful.

December 16, 2014 05:18 PM
Naman Dwivedi

This didn't work for me :( My game is very simple, but has a bunch of textures which increase the file size massively. I tried converting them to .bytes but the file size didn't change at all

June 18, 2016 04:50 AM
Demosthenes

This didn't work for me :( My game is very simple, but has a bunch of textures which increase the file size massively. I tried converting them to .bytes but the file size didn't change at all

Hey,

What file extensions do your textures have and what are you using them for?

June 18, 2016 10:43 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement
Advertisement