Jump to content
  • Advertisement
  • entries
    112
  • comments
    236
  • views
    136216

About this blog

Still making games...

Entries in this blog

 

Reducing Unity game file size

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.

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):



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.

Demosthenes

Demosthenes

 

Quick update...

EDIT: Here's a video of the latest build.

Just a quick update, Attack of the 50ft Verbose Dinosaur is back in playtest, here are the changes since the last version:

Bugs fixed:
- Chopper collided with bubbles that weren't shot.
- Credits only showed once.
- Stack overflow when many bubbles were attached to the same building.
- Sometimes the fireball effect kept appearing on the dinosaur's head.
- The chopper appeared immediately if you paused the game for some time.

Modifications:
- Vowels no longer include W and Y (only a,e,i,o,u)
- Letters not shown when level is paused to avoid cheating
- Chopper moves slower in the easier modes.
- Easy time limit increased again due to popular demand.
- Font size increased when time is running out.
- Font colors changed in credits for improved readability.
- Contrast increased for the letters on the dinosaur's back.
- More information about letter power added to the tutorial.
- If the helicopter comes when you destroy a building, it crashes but returns almost immediately with the same bonus.
- Formed words are no longer desselected when the chopper is hit with a bubble.
- Parallax scrolling on the background.

If you are an Xna Creators Club Member and want to give it a try, click here.

Demosthenes

Demosthenes

 

Playtest....

Attack of the 50ft Dinosaur is now in playtest. If you are an Xna Creators Club member and you have nothing better to do, give it a try here.

Here's the game cover, I'm not sure if it's the final one:

EDIT - The second image should be the final one, my brother changed it to make the dinosaur look even more deranged.



Demosthenes

Demosthenes

 

Phat Ninja

So I finished incorporating a new ninja... phat ninja! When playing co-op, one player will play as the previous "thin" ninja and the other as the phat ninja.

">Video here. If you can watch it on full screen to see all the body animations.

Demosthenes

Demosthenes

 

Peer review again...

Bennu is back in peer review.

Here's a list of what changed since the last version:
- My cousin did all of the songs for Bennu, but one of them was originally written for a "Master of Orion"-style game I never finished. It had a singing part that I removed because the lyrics had nothing to do with Bennu.
- Instead of pressing "down" to roll on the ground, now you use "B", which is easier to do than moving the thumbstick. Many of these version changes were suggested by the guy who did "Weapon of choice" and is currently doing "Grapple Buggy", which is looking totally awesome.
- Now there's music playing on the tutorial.

Bugs fixed:
- Game no longer crashes on storage selection when playing with the second controller.
- All "key" references replaced by "button" (a vestigial mark from when Bennu was destined for the PC).
- The reason why you lost the level is now much more visible.
- The "New Game" button no longer asks for confirmation when no progress has been made.

Hope it passes this time, if it does please try it. :P

Demosthenes

Demosthenes

 

Particle effects, etc.

Over the last week I added a proper grappling hook to the ninja rope and also integrated the Mercury Particle Engine V3.0, which comes with a very nice editor.

The particle effects consist dust clouds that go off whenever the ninja walks or jumps off a wall and friction sparkles when the ninja wall slides.

">Video here.

Demosthenes

Demosthenes

 

Particle effects

Screenshot!



Since the last update, I worked mainly on the hud and on particle effects for color change and for the eclipse in the hud (when it ends, the player has ran out of time to complete the current level - right now the level never ends, but you get the idea). Here's a video of the new stuff.

Demosthenes

Demosthenes

Outsider

4 years later...

Get In the Ring has been thrown in the pile of projects on hold. We've been developing Outsider, an adventure game where every puzzle is different.

In the past year we've been selected by Eurogamer as one of the 8 best Portuguese games and as one of the 11 best in Playstation Talents, where we also placed in the top 4 best in the Press award and one of the 3 best in the Most Innovative game award.

Here's the most recent trailer: Development wise, I'm now working with an illustrator. We're using Unity with SVG Importer. Basically our workflow is that the illustrator does the graphics in... Illustrator, and the SVG Importer plugin allows us to use exported SVG files directly in the game. We've opted to use vectorial graphics to allow for unlimited zooms and seamless transitions. We're currently working on the final third of the game's story and we hope to have a release date soon.   Cheers, Ric

Demosthenes

Demosthenes

 

Options screen

Hello there! Since the last time I scratched the walls of this journal with my fingernails I've added sound effects to the game (though not complete yet) and finished the points system, which adds points when the player performs a special move, such as hitting two blocks without breaking the chain. Click here for a video, though since this I've moved the points to the bottom bar because they just cluttered the display too much. :)

Lately I've been working on the options screen, which you can see below. It's not yet in the game, just a composition.


The idea is that you will slide the music and fx indicators to control the respective volumes and pull out the player1/player2 or keyboard/360 controller when you want to change input options.

Demosthenes

Demosthenes

 

One Man Armada...

So I submitted Attack of the 50ft Verbose Dinosaur to the DreamBuildPlay contest. There are some impressive entries (at least graphically) this year, so I'm not that confident, but hope dies last. :)

In the meantime, I started a new game, One Man Armada (OMA), with a different physics engine, Box2D.Xna. My first impression, when compared to the Farseer Physics Engine, is that Box2D.Xna is much, much faster (it is optimized for the Xbox 360), but has less features. Farseer is close to releasing a new version, but right now I'd recommend Farseer for a PC game (or an Xbox game where you really need the more advanced features) and Box2D.Xna for an Xbox game where performance is essential.

All my games start with a basic gameplay idea that evolves according to whatever I think is working best as I playtest the game. :) I guess it's mostly the same for everyone.

OMA's basic idea is what would happen 9 months after Geometry Wars got drunk and had a foursome with Peggle, Breakout and Freespace. It's a dual-thumbstick shooter (with ships, not abstract stuff) where you shoot balls, like in Breakout, that bounce off ships, causing more damage with each bounce. So you must try to hit as many ships as possible with each shot while avoiding enemy fire.

Right now I'm working on a basic prototype and getting to grips with the physics engine. If the game is fun I'll follow up with it; if not I'll think of some other game. I did a logo anyway:


Demosthenes

Demosthenes

 

On the swamp

A-aight. Been working on Bennu on the free time I have between work and Call of Duty 2. :)

I wanted to finish a single level before anything else. The setting for the level is a swamp:



I've also been building a sort of integrated level editor, where I can play and drag, scale and rotate objects around at the same time. The gun in the previous screenshot was made using it. I can save the level as is at any time to a XML file.

The first prototype I made of Bennu had him changing color randomly. Now for Bennu to change color he has to touch bonuses spread across the level, in this case mushrooms. Click here to see a video of the latest gameplay. The music was just what I was listening at the time and is not part of the game. :)

Demosthenes

Demosthenes

 

Oma - First gameplay video

Here's the ">first development video for One Man Armada. Basically the destroyer ship spawns the fighter ships that chase the player ship. Each fighter ship tries to avoid the other fighter ships and the destroyer ship. The box2d.xna engine provides a raycast mechanism that helps to prevent collisions.

Demosthenes

Demosthenes

 

Now with rope!

Implemented wall sliding and the ninja rope. ">Video here.

Wall sliding was implemented by detecting the angle of the wall to check if it is "vertical enough" for the ninja to slide in and then applying a force to the ninja, in the direction of the wall. Right now it still has some bugs when colliding with the edges where the blocks connect to each other.

The ninja rope uses a fixed linear spring from the Farseer Physics Engine. The ninja is set to a ragdoll state when attached to the rope.

Demosthenes

Demosthenes

 

New Zombie Animations

My brother finished the new zombie animations. There are more frames for movement, attacking and dying. Video below:

[media][/media]

Also, merry Christmas everyone!

Demosthenes

Demosthenes

 

New tile upgrade - Flame trail

Made a new tile upgrade that when the player steps on starts leaving behind a flame trail that damages zombies. The tile sprite is not final. Right now, the big red "X" represents a cooldown during which the player can't step on the tile again. Video here:

[media] [/media]

Demosthenes

Demosthenes

 

More FX

I've changed the blocks' appearance and added some particles effects to give the idea the Bennu is burning the blocks away. Video here.

Demosthenes

Demosthenes

 

Menus, maps and progress...

Despite the lack of updates here, I've been dedicating a lot of time to Bennu lately. I've been working on the main menu and player map:




Each player, on the left, will only see the locations he has reached yet. I'm planning to have the 5 locations depicted in the map in the final game.

I've also been working on the game mechanics, which are nearly finished, and should start designing levels for the first location (Abu Simbel) soon.

Demosthenes

Demosthenes

 

Menus and cutscenes

Been working on menus and the intro cutscene.

On each map a player gets stars if he completes the map in less than the star time. This was done to emphasize that besides winning the map the goal is to complete each map as quick as possible.

The victory screen:



The thumbs up come out of the woodwork in a small animation.

House banners for the intro cutscene:


Demosthenes

Demosthenes

Making a website from your game, for your game

Hello there. As mentioned in a previous blog entry, we're developing an adventure game called Outsider, where every puzzle is different. Outsider is being developed in Unity using a plugin called SVG importer, meaning most graphics in the game are vectorial. In the past year, we have improved the graphics a lot: January 2018                     April 2019   Details were added to the graphics, the lighting was tuned and we also started using Unity's recent post-processing profiles. Meanwhile, the website we built for the game still used screenshots from the old version and looked ancient:                   The website was also minimal, consisting of just images and gifs from the game interspersed with minimal text. So, since all the recent browsers support SVG, we decided to use the graphics from this scene, directly from Unity, to create a new website: If you want to take a look, it's live at www.onceabird.com. So, on to the code. Unity supports extending the GUI of your behaviours, so we inherited the Editor class in a WebsiteGeneratorEditor class to create a single button to export the scene layout: [CustomEditor(typeof(WebsiteGeneratorScript))] public class WebsiteGeneratorEditor: Editor { public override void OnInspectorGUI() { DrawDefaultInspector(); serializedObject.Update(); WebsiteGeneratorScript websiteGeneratorScript = (WebsiteGeneratorScript)target; if(GUILayout.Button("Export Scene")) { websiteGeneratorScript.ExportScene(); } } } Each Editor class references another script as a target, in our case it's named WebsiteGeneratorScript. Here's the full source code: WebsiteGeneratorScript.cs, I'll go over it bit by bit. But first some context. The SVG plugin we're using creates an SVG Renderer component (as opposed to the normal Sprite Renderer or other Renderer components) on each behaviour that uses an SVG asset:                   Although some code here is specific to SVG Renderer, most of it is based on Unity's standard position, rotation and scale Transform, so it should be easy to adapt to your project. The exported scene's SVG Renderers are contained in a single root transform named Room: And here's how the WebsiteGeneratorScript component is configured in this scene:                                     We'll gloss over the details for now. The WebsiteGeneratorScript class contains a recursive method named ExportTransform: string transformText = ""; ExportTransform(GameObject.Find("Room").transform, ref transformText); The method searches for an SVGRenderer on each transform, starting from the Room root transform. The transformText variable contains the generated HTML text that will be inserted into an HTML file in the end. Not all the elements in the scene will be exported, so the first thing the method does is return if the transform is in the excludedTransforms list (the contents of which you can see in the WebsiteGeneratorScript component image above): private void ExportTransform(Transform transform, ref string transformText) { if(Array.IndexOf<Transform>(excludedTransforms, transform) >= 0) { return; } If an SVGRenderer component is found, it generates the corresponding HTML img tag: SVGRenderer renderer = transform.GetComponent<SVGRenderer>(); if(renderer != null && !transform.name.ToLower().Contains("interactive")) { float width = renderer.vectorGraphics.bounds.size.x * scale * transform.localScale.x; float height = renderer.vectorGraphics.bounds.size.y * scale * transform.localScale.y; float left = transform.position.x * scale - width / 2.0f - (renderer.vectorGraphics.pivotPoint.x - 0.5f) * width; float top = -transform.position.y * scale - height / 2.0f - (renderer.vectorGraphics.pivotPoint.y - 0.5f) * height; string[] pathParts = UnityEditor.AssetDatabase.GetAssetPath(renderer.vectorGraphics).Split(new char[] { '/' }); string path = ""; for(int i = 3; i < pathParts.Length - 1; i++) { path += pathParts[i] + "/"; } string fileName = pathParts[pathParts.Length - 1]; path += fileName.Substring(0, fileName.IndexOf(".")) + ".svg"; if(positionDifferencesDic.ContainsKey(path)) { Vector2 diff = positionDifferencesDic[path]; left += diff.x; top += diff.y; } string rotationText = transform.eulerAngles.z == 0.0f ? "" : "transform: rotate(" + (-transform.eulerAngles.z) + "deg);" + "transform-origin: " + (renderer.vectorGraphics.pivotPoint.x * 100) + "% " + (renderer.vectorGraphics.pivotPoint.y * 100) + "%;"; string className; if(cssClassesDic.ContainsKey(path)) { className = " " + cssClassesDic[path]; } else { className = ""; } transformText += "<img src='" + path + "' " + "class='transform-image" + className + "' " + "style='left: " + left + "%; top: " + top + "%; width: " + width + "%; height: " + height + "%; " + "z-index: " + renderer.sortingOrder + "; " + rotationText + "' />\n"; } Since we wanted the website to be responsive, the left, top, width and height style properties are calculated in percentage, and every HTML img element uses position absolute. Width and height are calculated first, using a scale variable that is applied to all exported SVG, determining the global exported website scale, the transform's local scale and the size of the SVG itself. If you're using sprites, the width and height of the base image should be used here. Next left and top are calculated. The scale variable is also used, as is the SVG Renderer's vectorGraphics.pivotPoint, which is the center point of any rotation. The path variable contains the URL to the SVG asset, which must be placed separately from this code on your website's file structure. The UnityEditor.AssetDatabase is used to find the file path on the Unity project and replicate it to the exported website path. An example exported HTML img tag looks like this: <img src='SVG/CeilingLamp.svg' id="ceiling-lamp-1" class='transform-image ceiling-lamp' style='left: -24.65349%; top: -58.18391%; width: 10.21898%; height: 38.1918%; z-index: 50;' /> Unity's sortingOrder corresponds to CSS's z-index. Here's the transform-image CSS class: .transform-image { position: absolute; overflow: visible; padding: 0px; margin: 0px; border: 0px; transform: translate3d(0,0,0); max-height: 100%; } (The rest of the source code for the webpage can be viewed on our website, none of the CSS or JS is minified or obscured.) The generated HTML was just the starting point:  We had to condense several SVGs into a single file to improve performance, since SVG rendering still has a way to go on most browsers. Some graphics were created just for the website, such as the social media links on the bottom right corner and the trophies. We used CSS animations and transitions to do zooms but these made the SVG blurry in the iOS version of Safari, so we had to upscale the "clickable" elements in relation to the rest of the elements on the page. The shaking effect was also done with CSS animations. To simulate lighting on the webpage we created a transparent SVG shaped like the lamp light and used blend modes to create the simulated effect.   If you want to know more about Outsider and/or our development process, please follow us on Twitter or Instagram ( or on www.onceabird.com ).
And if there's something unclear or missing in this article please let us know. Thanks for reading!

Demosthenes

Demosthenes

 

Ludum Dare

So I actually finished a game in 48 hours for Ludum Dare!

The theme was 10 seconds. The game is called "George RR M&M's Twitchy Thrones" and it's a real-time strategy game where the population grows every 10 seconds, so speed is of the essence.

If you'd like to try it (I'd love it if you did) download it here: http://onceabird.com/ft/TwitchyThrones.zip

XNA Framework 4.0 Redistributable is needed. Download here: http://www.microsoft.com/en-us/download/details.aspx?id=20914

Screenshots:




Demosthenes

Demosthenes

 

Levels...

I've been working on moving color dispensers. Right now Bennu has 27 levels and won't have much more. I'm doing a final boss and my brother is redoing some backgrounds. After that Bennu should be finished in 2-3 months as I work on some extras.

Click here for a video of some of the new levels. It's about 24 megs and 3 minutes long, so if you feel sleepy leave it for some other time. ^^

Demosthenes

Demosthenes

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!