Author Topic: Best method to embed resources?  (Read 2413 times)

GrimPanda

  • Member
  • **
  • Posts: 46
  • Karma: +1/-0
    • View Profile
    • Grim Panda Games
Best method to embed resources?
« on: Tue, Jul 12, 2011 »
I hope I can phrase this in a manner that makes sense.

I've always wondered which is better when you embed resources in flash/flixel.  Some sprites, like buttons or panel backgrounds I may use in multiple classes/states.  Yet, others will only be used in one class/state.

So my question is... is it better to A) Throw all the sprite/resource embeds in one Resources.as class and just include it on each class.  B)  Embed each resource individually per class, even if I'm embedding the same resources multiple times throughout different classes.  C)  Make a Resource class for common embeds (fonts, buttons, etc) and individually embed resources unique to only one class.

I'm wondering if including a central Resource class is a memory waste if I don't need all the other embeds on it at that time?  I prefer the central location for organization, but if it's a memory waste, then I'll change my code.

Hope that makes sense. 
"Mortis Ailuropoda"

photonstorm

  • Administrator
  • Key Contributor
  • *****
  • Posts: 1502
  • Karma: +1/-0
    • View Profile
    • Photon Storm
Re: Best method to embed resources?
« Reply #1 on: Tue, Jul 12, 2011 »
If you use the same PNG across multiple classes (using the Embed directive at the top) then it will be compiled every single time for each class. So if the PNG is 200KB then it'll be 200KB x however many classes you embed it in.

Crappy huh? But it's how it works.

To get around it you can either put the "common" assets into a single class and reference them statically. Works fine, pretty easy to maintain (to a point). Or you can package up all the assets in the Flash IDE library, give them linkage names, and export as an SWC which is then linked into your project. Cleaner, easier to manage imho, and you get the benefits of compressed JPEGs with transparency, which Embed can't handle. Of course if you don't have the IDE it makes this part hard!
http://www.photonstorm.com



"Tell me and I will forget, show me and I might remember, involve me and I will understand" - Confucius

chiguire

  • Member
  • **
  • Posts: 26
  • Karma: +0/-0
    • View Profile
    • My Personal Site
Re: Best method to embed resources?
« Reply #2 on: Tue, Jul 12, 2011 »
Photonstorm replied before me :-P I thought that the EMbed directive compiled once per generated .SWF, so Embedding in two classes did not increase the SWF byte size.

So I'll add something else.

It's very convenient to have all resources in one class if you need to access them from anywhere in your project. In a project I made, I used a Singleton that returned all the resources you needed: images, sound, animation, etc., and the Singleton class was generated by a Python script that read a CSV list with the file values and the type. If you'd like the script code I cant embed it here for you.
« Last Edit: Tue, Jul 12, 2011 by chiguire »

GrimPanda

  • Member
  • **
  • Posts: 46
  • Karma: +1/-0
    • View Profile
    • Grim Panda Games
Re: Best method to embed resources?
« Reply #3 on: Wed, Jul 13, 2011 »
If you use the same PNG across multiple classes (using the Embed directive at the top) then it will be compiled every single time for each class. So if the PNG is 200KB then it'll be 200KB x however many classes you embed it in.

I had wondered if this was the case.  Glad I asked  :o

To get around it you can either put the "common" assets into a single class and reference them statically. Works fine, pretty easy to maintain (to a point). Or you can package up all the assets in the Flash IDE library, give them linkage names, and export as an SWC which is then linked into your project. Cleaner, easier to manage imho, and you get the benefits of compressed JPEGs with transparency, which Embed can't handle. Of course if you don't have the IDE it makes this part hard!

That is what I'm doing currently.  I like the SWC idea too, never thought of that.  I'm just beginning to understand the power of these little guys.   8)

Photonstorm replied before me :-P I thought that the EMbed directive compiled once per generated .SWF, so Embedding in two classes did not increase the SWF byte size.

I had been operating under the same presumption.  But there was this funny feeling that I should ask... heh.

...the Singleton class was generated by a Python script that read a CSV list with the file values and the type. If you'd like the script code I cant embed it here for you.

That's what I do currently as well, but without the .py and CSV fanciness you are using.  I'd love to see what you are using if you don't mind.  The game I'm working on right now has dynamically updated content, so while SWC's will work for my more static games, I think this would be a nifty approach for something like this and during devel.

Thanks!  ;D
"Mortis Ailuropoda"

chiguire

  • Member
  • **
  • Posts: 26
  • Karma: +0/-0
    • View Profile
    • My Personal Site
Re: Best method to embed resources?
« Reply #4 on: Thu, Jul 14, 2011 »
This Python script reads a tab-separated file and generates a class that holds Embeds to the resources included the in CSV file. It recognizes GIF files or MP3s, any other case it embeds it as a ByteArray.

When I did this code, I did know about the existence of the GIF player library (http://code.google.com/p/as3gif/), so I exported my animation frames as a GIF per frame (I know, amateurishly). But this project is already done, so I know better next time.

The other thing is that all the embeds are included in a .SWF file, while the game is in another. This allows me to load the game very quickly and show the progress of the assets loading. I did this by creating an interface (IAssetLoader), and having the class generated by this .py implement such interface.

The text format is this one: (- tab - means a \t character)

Code: [Select]
(asset ID here) - tab - (filename)

For the series of images, I set a variable in the filename with the $ sign, and then specified the starting index and the end index, like this:

Code: [Select]
(asset ID here) - tab - (filename$.gif) - tab - startingIndex - tab - endIndex

For example:

Code: [Select]
mainmenu_insertcoinanim mainmenu/start_cutscene_f$.gif 1 73
pianohero_bar pianohero/bar.gif
gameover_song gameoversong.mp3

This is the Python script for generating the files:

Code: [Select]
# embedAssetsInSWF.py
# Takes a source file and puts all its contents in an AS3 class ready to be compiled.
# Execute as: packFiles.py [Source-File] [Name of Class] [Base-Dir]
# Example: packFiles.py sourcefile.txt CachicamoAssets ../exported-assets/

import sys
import os

def getClassEmbeds(rL):
result = ""
for r in rL:
if (r[1] == 'byteArrayHash'):
result += \
""" [Embed(source='"""+baseDir+r[4]+"""',mimeType='application/octet-stream')]
private var """+r[2]+""" : Class;

"""
elif (r[0] == 'series'):
for i in xrange(int(r[5]), int(r[6])):
result += \
""" [Embed(source='"""+baseDir+r[4].replace("$", str(i))+"""')]
private var """+str(r[2])+str(i)+""" : Class;

"""
else:
result += \
""" [Embed(source='"""+baseDir+r[4]+"""')]
private var """+r[2]+""" : Class;

"""
return result

def getHashAdds(rL):
result = ""
for r in rL:
if (r[0] == 'series'):
for i in xrange(int(r[5]), int(r[6])):
result += \
""" """+r[1]+"""['"""+r[3]+str(i)+"""'] = new """+r[2]+str(i)+"""();
"""
else:
result += \
""" """+r[1]+"""['"""+r[3]+"""'] = new """+r[2]+"""();
"""
return result

sourceFileName = sys.argv[1]
className = sys.argv[2]
baseDir = sys.argv[3]
resList = []

sourceFile = open(sourceFileName, 'r')

for line in sourceFile:
assetType = "";
if (line.strip() == ""):
continue
l = line.strip().split("\t")

if (l[1].endswith(".gif")):
assetType = "bitmapHash"
elif (l[1].endswith(".mp3")):
assetType = "soundHash"
else:
assetType = "byteArrayHash"

if (len(l) == 4):
resList.append(('series', assetType, l[0], l[0].replace("_", "."), l[1], l[2], l[3]));
else:
resList.append(('unique', assetType, l[0], l[0].replace("_", "."), l[1]));

classEmbeds = getClassEmbeds(resList)
addedToHash = getHashAdds(resList)

print """
package
{
import com.ciroduran.io.IAssetLoader;

import flash.display.Bitmap;
import flash.display.Sprite;
import flash.media.Sound;
import flash.utils.ByteArray;

public class """+str(className)+""" extends Sprite implements IAssetLoader {

"""+str(classEmbeds)+"""

private var soundHash : Object /* of Sound */;
private var bitmapHash : Object /* of Bitmap */;
private var byteArrayHash : Object /* of ByteArray */;

public function """+str(className)+"""() {
soundHash = new Object();
bitmapHash = new Object();
byteArrayHash = new Object();

"""+str(addedToHash)+"""
}

public function getBitmap(key:String) : Bitmap {
return bitmapHash[key] as Bitmap;
}

public function getBitmapSeries(key:String, end:uint, start:uint=0) : Array {
var a : Array = new Array();
for (var i : int = start; i < end; i++) {
a.push(bitmapHash[key+i]);
}
return a;
}

public function getSound(key:String) : Sound {
return soundHash[key] as Sound;
}

public function getByteArray(key:String) : ByteArray {
return byteArrayHash[key] as ByteArray;
}
}
}
"""

And running the script generates this class:

Code: [Select]
package
{
import com.ciroduran.io.IAssetLoader;

import flash.display.Bitmap;
import flash.display.Sprite;
import flash.media.Sound;
import flash.utils.ByteArray;

public class GameAssets extends Sprite implements IAssetLoader {

// (Embedded resources go here)

private var soundHash : Object /* of Sound */;
private var bitmapHash : Object /* of Bitmap */;
private var byteArrayHash : Object /* of ByteArray */;

public function GameAssets() {
soundHash = new Object();
bitmapHash = new Object();
byteArrayHash = new Object();

// (Embedded resources are added in these hashes)
}

public function getBitmap(key:String) : Bitmap {
return bitmapHash[key] as Bitmap;
}

public function getBitmapSeries(key:String, end:uint, start:uint=0) : Array {
var a : Array = new Array();
for (var i : int = start; i < end; i++) {
a.push(bitmapHash[key+i]);
}
return a;
}

public function getSound(key:String) : Sound {
return soundHash[key] as Sound;
}

public function getByteArray(key:String) : ByteArray {
return byteArrayHash[key] as ByteArray;
}
}
}

The IAssetLoader interface is rather simple:

Code: [Select]
package com.ciroduran.cccgame.io
{
import flash.display.Bitmap;
import flash.media.Sound;
import flash.utils.ByteArray;

public interface IAssetLoader
{
function getBitmap(key:String) : Bitmap;
function getBitmapSeries(key:String, end:uint, start:uint=0) : Array;
function getSound(key:String) : Sound;
function getByteArray(key:String) : ByteArray;
}
}

There is probably an easier way to do this, but I had some fun with this project :-P If you have any improvements on this, let me know :)

GrimPanda

  • Member
  • **
  • Posts: 46
  • Karma: +1/-0
    • View Profile
    • Grim Panda Games
Re: Best method to embed resources?
« Reply #5 on: Fri, Jul 15, 2011 »
Awesome thanks!  I'll have a look into this :o)
"Mortis Ailuropoda"