Porting game assets from Flash to Tizen

Introduction

In this article we will focus on porting visual game assets from Flash to the Tizen platform. In order to port your games and make them work swiftly on the Tizen platform, we will show you how to port the visual game assets to Tizen and pixi.js in a smart way. Alongside this article, we have prepared for you the sample application basketgameassets.wgt developed with the Tizen SDK 2.3. This application reflects all the topics presented in this article. We have also attached a Flash project file (FLA), so you can use it to go through the porting process along with this article.

 

The game assets

Please note that for the purpose of this article we need to use the Adobe Flash IDE. We have used specifically the Adobe Flash CS6 IDE, but you can use the future versions of the IDE also. If you don’t have the Flash IDE you can obtain the latest 30 day trial version at https://creative.adobe.com/products/download/flash . The first thing to do while porting your visual game assets from Flash to Tizen is to find inside your Flash project (the FLA file) the actual MovieClips holding your flash animations. It is vital to find the correct MovieClips. Usually those are the bottom, nested MovieClips in the hierarchy of your animation. We could export our animations in the traditional way, as a series of PNG or JPG images. But this is not the best idea while porting to a mobile device. Mobile devices have lower performance and less memory than desktop computers. Therefore it is wise to use the texture atlas paradigm while creating assets for the multimedia mobile applications. Using texture atlases is handy. The basic concept behind this is that in opposite to having all your animation frames in separate files, here you have only one big image with all the animation frames on it. And those frames are generated from all the assests of the game. Along with this image file there is generated a descriptor file (usually in JSON format), that stores the information about the name of the animations, frame coordinates and many more. So the game engine can then import the image file and based on the JSON file it can retrieve and run the animations.

 

Exporting animations from the Flash IDE

So what are the steps to successfully export your flash animations in a texture atlas for pixi.js to a Tizen Web Application?

First of all find in the Flash IDE library all the MovieClips with the animations you wish to export. Select them while holding the shift key. Then, right click one of them and choose from the context menu the “Generate Sprite Sheet…” option (Figure 1).

Figure 1 – the Flash IDE library view with two selected MovieClips for generating a sprite sheet

 

After clicking the “Generate Sprite Sheet…” option a second window will appear, showing you a prepared texture atlas with all the animation frames from the animations you have selected in the first step. As you can see on figure 2 you can choose from several options to enhance your texture atlas. For our example we have chosen the MaxRects algorithm for packing up the textures. This algorithm can be shortly explained as trying to put as many rectangles in a larger rectangle. By selecting this option you are optimizing your texture atlas by reducing its pixel width and height. As for the data format we have chosen the JSON format. It is the most comfortable format to load into pixi.js. It gives each frame of your animation a unique name along with a four digit number. It is way better to use such prepared JSON file instead for example a JSON-Array, which is also available. The JSON-Array format is almost the same as the default JSON, but instead of giving each frame a name based ID, you will have to reference each frame by an integer. So you can see selecting JSON is better for controlling frames in your project. You can also select options like Trim and Rotate. The first one trims the amount of transparent space around a frame, giving you more pixel space on your sprite sheet. The second one does the same but only by manipulating the rotation of frames if needed. In some situations the Rotate option can be really handy in decreasing the texture atlas size. You probably saw that we also checked the Stack Frames option. Basically, what it does is reusing identical frames in the animation. Identical frames in the sprite sheet are being cut out, leaving only one of them. 

 

Figure 2 – the Generate Sprite Sheet window with the texture atlas ready to export for the basketgameassets.wgt

 

Then rest of the magic happens inside the JSON. The appropriate identical frames to be used in the animation are being referenced several times inside the JSON instead of drawing them multiple times on the sprite sheet. Therefore we get a big save on the size of the whole texture atlas. After selecting the Stack frames option numbers showed up next to each frame. Those numbers tell you how many times in your animation this frame will occur. All that data will be stored in the JSON. In our example the number next to the tentacle frames is 5. Which means that each tentacle frame will be used 5 times in the animation. This is a huge amount of saved space in the texture atlas and the memory of our device. As for the other options, you can change the Image dimensions to your own and trim the contents of the texture atlas to the smallest possible size, but it is a good practice to leave the Image dimensions on Auto size. Why? Because if you want to port your sprite sheets not only for Tizen and JavaScript, but also for other platforms, then in many cases those platforms need texture atlases which pixel size in width and height is the power of 2. So using a custom texture atlas size can be problematic in those cases. You can also choose the Background color and the file format of your sprite sheet. But for our example we will use a transparent background along with the PNG 32 bit image format. Now you can set the file name of your sprite sheet and JSON file next to the Browse… button and you can use that button to select the path to save the files to. The last thing to do is to press the Export button and generate the texture atlas.

 

Our exported texture atlas as we said previously consists of two files. The PNG file with the image data (figure 3) and the JSON file containing information about the frames. 

Figure 3 – an exported sprite sheet with two animations (512 x 1024) PNG format

 

Please note that the JS lint in the Tizen IDE sometimes shows errors in the generated Flash IDE JSON file. This will prevent you from building your Tizen project. If you encounter such a problem please use the http://jsbeautifier.org/ . Just copy the contents of your JSON file and paste it into the web page and click the Beautify JavaScript or HTML button. Then copy the changed code and replace the old code in the JSON file. Your JSON should look similar to the example below. Finally save the JSON file. 

 

{
  "frames": {

    "sideMonster0000": {
      "frame": {
        "x": 0,
        "y": 678,
        "w": 175,
        "h": 159
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 175,
        "h": 159
      },
      "sourceSize": {
        "w": 175,
        "h": 159
      }
    },
    "sideMonster0001": {
      "frame": {
        "x": 0,
        "y": 678,
        "w": 175,
        "h": 159
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 175,
        "h": 159
      },
      "sourceSize": {
        "w": 175,
        "h": 159
      }
    },
    "sideMonster0002": {
      "frame": {
        "x": 0,
        "y": 678,
        "w": 175,
        "h": 159
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 175,
        "h": 159
      },
      "sourceSize": {
        "w": 175,
        "h": 159
      }
    },
    "sideMonster0003": {
      "frame": {
        "x": 175,
        "y": 678,
        "w": 175,
        "h": 159
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 175,
        "h": 159
      },
      "sourceSize": {
        "w": 175,
        "h": 159
      }
    },
    "sideMonster0004": {
      "frame": {
        "x": 175,
        "y": 678,
        "w": 175,
        "h": 159
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 175,
        "h": 159
      },
      "sourceSize": {
        "w": 175,
        "h": 159
      }
    },
    "sideMonster0005": {
      "frame": {
        "x": 175,
        "y": 678,
        "w": 175,
        "h": 159
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 175,
        "h": 159
      },
      "sourceSize": {
        "w": 175,
        "h": 159
      }
    },
    "sideMonster0006": {
      "frame": {
        "x": 175,
        "y": 837,
        "w": 175,
        "h": 148
      },
      "rotated": false,
      "trimmed": true,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 175,
        "h": 159
      },
      "sourceSize": {
        "w": 175,
        "h": 159
      }
    },
    "sideMonster0007": {
      "frame": {
        "x": 175,
        "y": 837,
        "w": 175,
        "h": 148
      },
      "rotated": false,
      "trimmed": true,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 175,
        "h": 159
      },
      "sourceSize": {
        "w": 175,
        "h": 159
      }
    },
    "sideMonster0008": {
      "frame": {
        "x": 175,
        "y": 837,
        "w": 175,
        "h": 148
      },
      "rotated": false,
      "trimmed": true,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 175,
        "h": 159
      },
      "sourceSize": {
        "w": 175,
        "h": 159
      }
    },
    "sideMonster0009": {
      "frame": {
        "x": 0,
        "y": 837,
        "w": 175,
        "h": 159
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 175,
        "h": 159
      },
      "sourceSize": {
        "w": 175,
        "h": 159
      }
    },
    "sideMonster0010": {
      "frame": {
        "x": 0,
        "y": 837,
        "w": 175,
        "h": 159
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 175,
        "h": 159
      },
      "sourceSize": {
        "w": 175,
        "h": 159
      }
    },
    "sideMonster0011": {
      "frame": {
        "x": 0,
        "y": 837,
        "w": 175,
        "h": 159
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 175,
        "h": 159
      },
      "sourceSize": {
        "w": 175,
        "h": 159
      }
    },
    "tentacle0000": {
      "frame": {
        "x": 0,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "sourceSize": {
        "w": 215,
        "h": 339
      }
    },
    "tentacle0001": {
      "frame": {
        "x": 0,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "sourceSize": {
        "w": 215,
        "h": 339
      }
    },
    "tentacle0002": {
      "frame": {
        "x": 0,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "sourceSize": {
        "w": 215,
        "h": 339
      }
    },
    "tentacle0003": {
      "frame": {
        "x": 0,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "sourceSize": {
        "w": 215,
        "h": 339
      }
    },
    "tentacle0004": {
      "frame": {
        "x": 0,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "sourceSize": {
        "w": 215,
        "h": 339
      }
    },
    "tentacle0005": {
      "frame": {
        "x": 215,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "sourceSize": {
        "w": 215,
        "h": 339
      }
    },
    "tentacle0006": {
      "frame": {
        "x": 215,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "sourceSize": {
        "w": 215,
        "h": 339
      }
    },
    "tentacle0007": {
      "frame": {
        "x": 215,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "sourceSize": {
        "w": 215,
        "h": 339
      }
    },
    "tentacle0008": {
      "frame": {
        "x": 215,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "sourceSize": {
        "w": 215,
        "h": 339
      }
    },
    "tentacle0009": {
      "frame": {
        "x": 215,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "sourceSize": {
        "w": 215,
        "h": 339
      }
    },
    "tentacle0010": {
      "frame": {
        "x": 0,
        "y": 339,
        "w": 215,
        "h": 339
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "sourceSize": {
        "w": 215,
        "h": 339
      }
    },
    "tentacle0011": {
      "frame": {
        "x": 0,
        "y": 339,
        "w": 215,
        "h": 339
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "sourceSize": {
        "w": 215,
        "h": 339
      }
    },
    "tentacle0012": {
      "frame": {
        "x": 0,
        "y": 339,
        "w": 215,
        "h": 339
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "sourceSize": {
        "w": 215,
        "h": 339
      }
    },
    "tentacle0013": {
      "frame": {
        "x": 0,
        "y": 339,
        "w": 215,
        "h": 339
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "sourceSize": {
        "w": 215,
        "h": 339
      }
    },
    "tentacle0014": {
      "frame": {
        "x": 0,
        "y": 339,
        "w": 215,
        "h": 339
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "sourceSize": {
        "w": 215,
        "h": 339
      }
    },
    "tentacle0015": {
      "frame": {
        "x": 215,
        "y": 339,
        "w": 215,
        "h": 339
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "sourceSize": {
        "w": 215,
        "h": 339
      }
    },
    "tentacle0016": {
      "frame": {
        "x": 215,
        "y": 339,
        "w": 215,
        "h": 339
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "sourceSize": {
        "w": 215,
        "h": 339
      }
    },
    "tentacle0017": {
      "frame": {
        "x": 215,
        "y": 339,
        "w": 215,
        "h": 339
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "sourceSize": {
        "w": 215,
        "h": 339
      }
    },
    "tentacle0018": {
      "frame": {
        "x": 215,
        "y": 339,
        "w": 215,
        "h": 339
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "sourceSize": {
        "w": 215,
        "h": 339
      }
    },
    "tentacle0019": {
      "frame": {
        "x": 215,
        "y": 339,
        "w": 215,
        "h": 339
      },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": {
        "x": 0,
        "y": 0,
        "w": 215,
        "h": 339
      },
      "sourceSize": {
        "w": 215,
        "h": 339
      }
    }
  },
  "meta": {
    "app": "Adobe Flash CS6",
    "version": "12.0.0.481",
    "image": "BasketGame.png",
    "format": "RGBA8888",
    "size": {
      "w": 512,
      "h": 1024
    },
    "scale": "1"
  }
}

 

Importing the texture atlas into a Tizen application with pixi.js

As said before we will be using the pixi.js WebGL renderer to playback the animation in a Tizen Web Application. We will omit the topic of the setup of the rendering engine as it was described earlier in an article about pixi.js. We will focus now on how to import both the sprite sheet and the JSON file into pixi.js and create a pixi.js MovieClip out of them.

First make sure that you put both, your PNG and JSON files, in your images directory of your Tizen Web project. Secondly as in the setup shown in the previous article you will use the AssetLoader pixi.js class to load up your assets. But this time you won’t specify manually an array of images to be loaded. Instead you will specify the path to the JSON file. Inside of this file there is the path to the sprite sheet. That is why both files must be in the same directory. If you want to have the sprite sheet in a different directory than the JSON file, then you will have to modify the path to the sprite sheet accordingly. The rest of the loading process stays the same.

 

[…]

    ////////////  GAME INITIALISATION  /////////////		   
	 
	// Before loading the JSON into your project 
        // please use http://jsbeautifier.org/ to enhance it

	   assetsToLoad = ["images/BasketGame.json"];
	
	// CREATE A NEW ASSET LOADER
	   loader = new PIXI.AssetLoader(assetsToLoad);
	// USE CALLBACK
	   loader.onComplete = onAssetsLoaded.bind(this);
	// BEGIN LOADING THE ASSETS
	   loader.load();

	function onAssetsLoaded() { 
		
		console.log("ASSETS LOADED!");

	[…]

	}
		 	
[…]

 

After loading the assets we still need to create a MovieClip out of our imported animation on the pixi.js stage. In the article we will show you the simplest way of creating the MovieClip out of the sprite sheet and the JSON file. But please note that a more advanced example is showed in the attached basketgameassets.wgt application.

The simplest way to get references to the individual frames is by creating an array with the frame names from the JSON. But for bigger frame numbers it is wise to use loops to generate those names or parse the JSON in order to obtain all the asset names and animation frame lengths. Beneath you can find the simplest example how to create a MovieClip out of the JSON

 

[…]

var tentacleFrames = ["tentacle0000","tentacle0001","tentacle0002","tentacle0003","tentacle0004",
    		      "tentacle0005","tentacle0006","tentacle0007","tentacle0008","tentacle0009",
	              "tentacle0010","tentacle0011","tentacle0012","tentacle0013","tentacle0014",
		      "tentacle0015","tentacle0016","tentacle0017"];

            // TENTACLE MOVIECLIP
	       tentacleclip = new PIXI.MovieClip.fromFrames(tentacleFrames);
               tentacleclip.animationSpeed = 0.5;
	       tentacleclip.play();
			 
	    // POSITION THE TENTACLE
	       tentacleclip.position.x = stageWidth/2 - tentacleclip.width/2;
	       tentacleclip.position.y = 1000;
			   
	       stage.addChild(tentacleclip);
		 	
[…]

 

Please remember that referencing the frame names you can create in pixi.js also textures and sprites. Treat the JSON assets as a traditional image based resource in pixi.js. The only difference is that you specify the frame name, not the image file name.

Summary

In this article we have presented how to create texture atlases from Flash assets for Tizen games and applications using the Adobe Flash CS6 IDE. We showed you how to generate sprite sheets. What obstacles may lay on your way to generate a valid and reliable texture atlas.  Finally we presented how to import the texture atlases into Tizen applications using the pixi.js WebGL renderer. And how to create an animated MovieClip out of it. We strongly encourage you to take a look inside the example application attached to this article to fully understand this process. We hope that this article will help you in efficient and fast porting of Flash assets to Tizen.