Wednesday, 30 April 2014

ATF experiments in Adobe AIR (in Android)

After we developed out latest app PerfectBrew for Android (helps you making teas!)
We realized it takes ages to load (well... 10 seconds!) but we want it faster. We had one unique texture atlas with PNG (2048x2048), fully crowded with everything....
 
In order to be able to expand our app later on we have decided to split it in 3 atlases (same contents for now)
  • 1 PNG (1024x2048)
  • 1 PNG (1024x1024)
  • 1 ATF (1024x2048)
We wanted to make sure we use ATF as much as possible without breaking the graphics (as it doesnt support preMultiplied Atlas!)

We compressed out PNG to ATF with:
png2atf.exe -c e -e -q 10 -4

And here are the results:

1 big PNG:
Loading time: 9-10 seconds.
VRAM used (right after loading) 3 PNGS: 31Mb

3 PNGs:
Loading time: 9-10 seconds.
VRAM used (right after loading) 3 PNGS: 37Mb

2 PNGs + 1 ATF:
Loading time: 8-9 seconds. 
VRAM used (right after loading) 2 PNGS + 1 ATF: 29Mb

We couldn't speed up the load unless we had some textures loaded AFTER the app started. But this was just an experiment where we forced the program to load everything at start.

We used ATFs in Beekyr (a ton of textures there!) and it was a lot more noticeable as there are a lot of ATFs in the game.


UPDATE:
I tried adding the -r argument and it makes a difference:
It generates very light ATF files that get loaded really fast when we execute the apps. But the images can look very blocky... To avoid this blockyness you need to remove the -q parameter:
png2atf.exe -c e -e -r -4


One thing that doesnt make a difference is when we pack the app into APK it doesnt make much of a difference in file space as APK is a compressed format.

I hope this can be useful for somebody!

Thursday, 20 June 2013

Enable Nape debugging with CitrusEngine.

After initializing the engine you need to enable a onEnterFrame listener to constantly refresh it all the debug images:

This is how I have done it:


           var params:Object = new Object();

           nape = new Nape("nape", params);
           nape.touchable = false;
           enableDebug();
           add(nape);
           


        private function enableDebug():void
        {
            debug = new ShapeDebug(GameVars.SCREEN_X,GameVars.SCREEN_Y);
            debug.drawBodies = true;
            debug.drawSensorArbiters = true;
            debug.drawConstraints = true;
            Starling.current.nativeOverlay.addChild(debug.display);
            stage.addEventListener(Event.ENTER_FRAME, loop );
        }

        public function loop( evt:Event ) : void {
            debug.clear();
            debug.draw(nape.space);
            debug.flush();
        }
It should be easy to adapt it to your needs. It's pretty straight forward!

Monday, 17 June 2013

Optimizing performance when developing high responsive apps: Pools

Everything needs to be pooled.

This means that you create objects when they don't exist. When an enemy or bullet get destroyed it will only get destroyed visually, not in the logics of the game.

The engine places that enemy or bullet in a place in the screen that is not possible to interact. Then disable it and wait until a new enemy of the same kind comes into the screen , then we recycle the object and reuse it as new by refreshing or resiting all its variables.

Why this? Creating destroying objects can be very heavy on the CPU when done several times in a frame (or loop). So by avoiding the creation of new objects we will have a smoother game play.

When creating objects we enable themt and when we don't need them, we can just disable them. In my particular game I've created this function to be used with CitrusObjects (from CitrusEngine).
       private function enableObject(status:Boolean):void
        {
            view.pauseAnimation(status); //textures will not update.
            view.visible = status; //will hide the object
            updateCallEnabled = status;//will stop updating each frame.
            _inUse = status;
            set_bodyEnabled(status); //disable collisions
        }

I hope it helps!

Thursday, 13 June 2013

From Box2D to Nape in CitrusEngine

Due the massive Garbage generated by Box2D creating memory fragmentation. I will be porting my shoot'em up game Beekyr (using CitrusEngine) from Box2D to Nape.


Firstly, I have changed these physics instance from:
var params:Object = new Object();
params.doSleep = false;
var box2D:Box2D = new Box2D("box2D",params);

box2D.gravity = new b2Vec2(0, 0);

box2D.touchable = false;
box2D.world.SetContinuousPhysics(false);
box2D.velocityIterations = 1;
add(box2D);
to Nape:
var params:Object = new Object();
var nape:Nape = new Nape("nape", params);
nape.gravity = new Vec2(0, 0);
nape.touchable = false;
add(nape);

I changed all my objects from extending Box2DPhysicsObject to NapePhysicsObject.
All my custom fixtures has been erased, and collision groups dissapeared.
_body.SetActive(true/false);
to Nape:
_body.space = null; //to disable
_body.space = _nape.space //to enable.

Updating rotation:
before, Box2D:

_body.setRotation(rads)

now, Nape, (I think this same code should work the box2D wrapper?):

_body.rotation = rads;


Updating velocity:
before, Box2D:

_body.SetLinearVelocity(new b2Vec2(_currentSpeed.x, _currentSpeed.y);

now, Nape, (I think this same code should work the box2D wrapper?):

_body.velocity = new Vec2(_currentSpeed.x, _currentSpeed.y);
But since the units change betweens engines, Im using pixels as unit and updating the position with my own engine:
x +=speed.x;
y +=speed.y;
This is uesful for shoot'em ups but I am very unsure what would happen if I was using real physics, with gravity, etc.

Updating collisions... they use now a different override, but the rest is the same:

before, Box2D:

override public function handleBeginContact(contact:b2Contact):void {
var collisionWith:* = Box2DUtils.CollisionGetOther(this, contact);

now, Nape:

override public function handleBeginContact(contact:InteractionCallback):void {
var collisionWith:* = NapeUtils.CollisionGetOther(this, contact);


now fixtures are not present as such so I needed to change the way I set up the filters and physics:

override protected function defineBody():void {
_bodyType = BodyType.KINEMATIC ;
}

override protected function createFilter():void {
super.createFilter();
_shape.sensorEnabled = true;
_shape.filter.sensorGroup = PhysicsCollisionCategories.Get("enemyBullet");
_shape.filter.sensorMask = PhysicsCollisionCategories.Get("player", "playerBullet");
}

I think I didn't have to change anything else...

Performance doubled on my smartphone, I went from 18-30FPS to constant 30FPS for the game.

Monday, 10 June 2013

Optimizing performance in CitrusEngine and Box2D

Performance tips.

I will explain my experience when I was building Beekyr game for AIR using CitrusEngine and Box2D....

This might be applied to other game engines.... but the way Box2D is used might be the same.
When I created first Beekyr stage prototype I had about 2000 objects moving smoothly in screen, Stage3D was just amazing. But I needed the sprites to collide between each other. Everyone recommended to use the physics engine Box2D, even it if was just for collisions (later on I replaced it for Nape engine (for AS3), which is at least twice as fast, click here to see how I converted from Box2D to Nape).

After adding the physics engine, I only was able to have about hundred of objects moving smooth and fast (still no code to make them react with each other).

I didn't realize that a physics engine would make things THAT slow, specially in smartphones, so I had to start optimizing everything:
I made everything to be classified as sensor:
_fixtureDef.isSensor = true;
That means that touching objects can overlap without having a bounce effect or something.

Enable only begin handle contact:
beginContactEnabled = true;
Objects will not react until I enabled at least one form of contact in Citurs Engine.
Then I want to create groups, that means groups with the same ID will not react to each other this is very good when you have loads of real bullets or lots of enemies, this will stop most collision functions to be triggered when the bullets or enemies overlap.
The way to do this is to edit the fixture filter, this is how enemy bodies are filtered:
    override protected function defineFixture():void {
           super.defineFixture();
           _fixtureDef.filter.groupIndex = GameVars.ENEMY_GROUP;
           _fixtureDef.isSensor = true;
        }

Don't ever use isBullet() unless is completelly necesary. This will add more calculations to the engine making it slower.



Monday, 27 May 2013

Fitting the game to in any screen resolution.

For my Beekyr Android game it all started as 720p as it would be a Flash game,  but then I realized that the game needed to work in 800x480  for my smartphone... After that, I realized I needed to make it work in more resolutions for the rest of all Android phones out there. I had to think a way to scale the game and assets.... Turned out that is wasn't that hard!
 
Basically, I had to work always with 720p in mind.

When the game loads I need to work out a conversion rate for the resolution. So first lines of code executed, even before the engine :

        public static const ORIGINAL_X:int= 1280;
        public static const ORIGINAL_Y:int = 720;
        public static var SCREEN_X:int;
        public static var SCREEN_Y:int;
      
        public static var REDUCTION_RATIO:Number;
    

   private function getReductionRatio():void
        {
           &nbspSCREEN_X = Capabilities.screenResolutionX;
           &nbspSCREEN_Y = Capabilities.screenResolutionY;
            var ratioX:Number = SCREEN_X / ORIGINAL_X;
            var ratioY:Number = SCREEN_Y / ORIGINAL_Y;

            if (ratioX < ratioY) {
                REDUCTION_RATIO = ratioX;
            }else {
                REDUCTION_RATIO = ratioY;  
            }
        }

Then apply this REDUCTION_RATIO variable to every loaded asset, positions,speeds and basically everything that involved pixels.

Examples:
params.height = size.w*REDUCTION_RATIO;
params.width = size.h*REDUCTION_RATIO;
_bulletSpeed = 38*REDUCTION_RATIO;


If using AssetManager, all the stretching will be done for you if you initialize it like this:
assetsManager = new AssetManager (1 / GameVars.REDUCTION_RATIO, false);

But if you are converting textures manually then convert them like this:
Texture.fromBitmap(bitmap,false,true,1/GameVars.REDUCTION_RATIO);
This will still use same amounts of Texture Memory in 720 than 480 or 320. If you have troubles with that , load different assets that are smaller and change the REDUCTION RATIO.

In my case, load / unload assets between stages, works well too.
I never reached 128MB of VRAM (more or less, the minimum memory these days: ipad1).

It takes time and real concentration to make it work well but when is done it works flawlessly.

Enjoy.

Tuesday, 21 May 2013

Career Shift : from Flash websites to AIR apps

I have been developing websites since 2004, I immediately jumped into Flash when I knew about it. This plug-in let me do really amazing things when you compared it with the HTML3-4 limits...

I worked for years as Flash developer along with very few PHP jobs. But Flash was becoming stronger. It was a solid OOP language and very tested plug-in without hardly any flaws.... until Steve Jobbs told otherwise and everyone believed him, they didn't realize it was all a war between Apple and Adobe, and everybody lost. Only the apple man won his own personal fight.

With all of this in mind I tried to make a game in Flash 10. I wanted to add many graphics and effects but I couldn't get anything very complex working due the lack of GPU acceleration. So I left the game in a playable state, but it was too simple. I had to discard the idea of making complex flash games.

Meanwhile Adobe didn't know how to handle the fight with Apple. Nevertheless Adobe released the best update ever made for flash: molehill - Stage3D. It was the beginning of triple A games in Flash. But it was all obscured with the war.

I realized about this and knew I was finally able to make complex games in flash... but flash sites were starting to die, all iProducts didn't accept flash so the world had to adapt to this (instead of the other way).

I didn't see the GPU acceleration happening in mobile phones until very late 2012. By then market almost already shifted back to old HTML. I was sure that didn't like this backwards step. In fact it was a waste of time for me, I lost all interest in the web: coding with JavaScript (no OOP) is a total nightmare when you add browsers variety to the equation.
So I had no choice other than leaving the web scene and move into apps/games.

I wanted to make games, I didn't mind what language so I researched into some of them: coronaSDK, Unity3D, phoneGap. But a friend told me about CitrusEngine framework for flash Stage3D. So I started working heavily for a month or two to get a demo that looked and and was responsive in Flash at very high frame rates... I was impressed as that was exactly what I had in mind a year back...
Only then,  I decided to make a real game. I got in touch with two friends that were able to work with me: Diego Gangl (amazing artist) and Mete Burch (modern musician). I ended up leading a team making the awesome game I always wanted and playable in most Android phones:
Beekyr

I made it in adobe AIR to be able to get to android devices. It was a really good decision. In the other hand it was also difficult to make the app perform well in these low powered devices. I was used to powerful PCs but I had to work hard to optimize everything and learn some very interesting techniques to make it run smooth everywhere.

So now, in 2013, the history carries on as Indie Developer.
I hope I can manage it well and live from making good quality games!