I am creating a flash game in ActionScript 3 with an infinite universe. Since the universe is infinite, the background is created dynamically using the following background engine:
BackgroundEngine.as
package com.tommedema.background
{
import br.com.stimuli.loading.BulkLoader;
import com.tommedema.utils.Settings;
import com.tommedema.utils.UtilLib;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.utils.Timer;
public final class BackgroundEngine extends Sprite
{
private static var isLoaded:Boolean = false;
private static var bulkLoader:BulkLoader = new BulkLoader("backgroundEngine");
private static var assetsBitmapData:Array = [];
private static var drawTimer:Timer;
private static var masterContainer:Sprite;
private static var containers:Array = [];
private static var stageWidth:uint;
private static var stageHeight:uint;
private static var stageCenterX:Number;
private static var stageCenterY:Number;
public static function moveXY(xAmount:Number, yAmount:Number):void
{
if (!masterContainer) return;
if (xAmount != 0) masterContainer.x += xAmount;
if (yAmount != 0) masterContainer.y += yAmount;
}
public static function loaded():Boolean
{
return isLoaded;
}
public final function load():void
{
if (isLoaded) return;
UtilLib.log("BackgroundEngine load.");
stageWidth = stage.stageWidth;
stageHeight = stage.stageHeight;
stageCenterX = stageWidth / 2;
stageCenterY = stageHeight / 2;
drawTimer = new Timer(Settings.BG_DRAW_IV);
drawTimer.addEventListener(TimerEvent.TIMER, updateBackground, false, 0, true);
drawTimer.start();
if ((bulkLoader.get("background/4.png")) && (bulkLoader.get("background/4.png").isLoaded))
{
loadAssets();
}
else
{
bulkLoader.add(Settings.ASSETS_PRE_URL + "background/1.gif", {id: "background/1.gif"});
bulkLoader.add(Settings.ASSETS_PRE_URL + "background/2.png", {id: "background/2.png"});
bulkLoader.add(Settings.ASSETS_PRE_URL + "background/3.png", {id: "background/3.png"});
bulkLoader.add(Settings.ASSETS_PRE_URL + "background/4.png", {id: "background/4.png"});
bulkLoader.addEventListener(BulkLoader.COMPLETE, assetsComplete, false, 0, true);
bulkLoader.start();
}
isLoaded = true;
}
public final function unload():void
{
if (!isLoaded) return;
UtilLib.log("BackgroundEngine unload method has been called.");
drawTimer.removeEventListener(TimerEvent.TIMER, updateBackground);
drawTimer.stop();
drawTimer = null;
assetsBitmapData = [];
for each (var container:Sprite in containers)
{
if (container)
{
masterContainer.removeChild(container);
container = null;
}
}
containers = [];
if (masterContainer)
{
removeChild(masterContainer);
masterContainer = null;
}
isLoaded = false;
}
private final function updateBackground(event:TimerEvent):void
{
if (masterContainer)
{
collectGarbage();
drawNextContainer();
}
}
private static function drawNextContainer():void
{
var curContainer:Sprite = hasBackground(stageCenterX, stageCenterY);
if (curContainer)
{
if (!hasBackground(stageCenterX - stageWidth * 0.75, stageCenterY - stageHeight * 0.75))
drawNewSquare(curContainer.x - curContainer.width, curContainer.y - curContainer.height);
if (!hasBackground(stageCenterX, stageCenterY - stageHeight * 0.75))
drawNewSquare(curContainer.x, curContainer.y - curContainer.height);
if (!hasBackground(stageCenterX + stageWidth * 0.75, stageCenterY - stageHeight * 0.75))
drawNewSquare(curContainer.x + curContainer.width, curContainer.y - curContainer.height);
if (!hasBackground(stageCenterX - stageWidth * 0.75, stageCenterY))
drawNewSquare(curContainer.x - curContainer.width, curContainer.y);
if (!hasBackground(stageCenterX + stageWidth * 0.75, stageCenterY))
drawNewSquare(curContainer.x + curContainer.width, curContainer.y);
if (!hasBackground(stageCenterX - stageWidth * 0.75, stageCenterY + stageHeight * 0.75))
drawNewSquare(curContainer.x - curContainer.width, curContainer.y + curContainer.height);
if (!hasBackground(stageCenterX, stageCenterY + stageHeight * 0.75))
drawNewSquare(curContainer.x, curContainer.y + curContainer.height);
if (!hasBackground(stageCenterX + stageWidth * 0.75, stageCenterY + stageHeight * 0.75))
drawNewSquare(curContainer.x + curContainer.width, curContainer.y + curContainer.height);
}
}
private static function drawNewSquare(x:Number, y:Number):void
{
containers.push(genSquareBg());
var cIndex:uint = containers.length - 1;
containers[cIndex].x = x;
containers[cIndex].y = y;
masterContainer.addChild(containers[cIndex]);
}
private static function hasBackground(x:Number, y:Number):Sprite
{
var stageX:Number;
var stageY:Number;
for(var i:uint = 0; i < containers.length; i++)
{
stageX = masterContainer.x + containers[i].x;
stageY = masterContainer.y + containers[i].y;
if ((containers[i]) && (stageX < x) && (stageX + containers[i].width > x) && (stageY < y) && (stageY + containers[i].height > y)) return containers[i];
}
return null;
}
private static function collectGarbage():void
{
var stageX:Number;
var stageY:Number;
for(var i:uint = 0; i < containers.length; i++)
{
if ((containers[i]) && (!isRequiredContainer(containers[i])))
{
masterContainer.removeChild(containers[i]);
containers.splice(i, 1);
}
}
}
private static function isRequiredContainer(container:Sprite):Boolean
{
if (hasBackground(stageCenterX, stageCenterY) == container)
return true;
if (hasBackground(stageCenterX - stageWidth * 0.75, stageCenterY - stageHeight * 0.75) == container)
return true;
if (hasBackground(stageCenterX, stageCenterY - stageHeight * 0.75) == container)
return true;
if (hasBackground(stageCenterX + stageWidth * 0.75, stageCenterY - stageHeight * 0.75) == container)
return true;
if (hasBackground(stageCenterX - stageWidth * 0.75, stageCenterY) == container)
return true;
if (hasBackground(stageCenterX + stageWidth * 0.75, stageCenterY) == container)
return true;
if (hasBackground(stageCenterX - stageWidth * 0.75, stageCenterY + stageHeight * 0.75) == container)
return true;
if (hasBackground(stageCenterX, stageCenterY + stageHeight * 0.75) == container)
return true;
if (hasBackground(stageCenterX + stageWidth * 0.75, stageCenterY + stageHeight * 0.75) == container)
return true;
return false;
}
private final function assetsComplete(event:Event):void
{
loadAssets();
}
private final function loadAssets():void
{
assetsBitmapData.push(bulkLoader.getBitmapData("background/1.gif"));
assetsBitmapData.push(bulkLoader.getBitmapData("background/2.png"));
assetsBitmapData.push(bulkLoader.getBitmapData("background/3.png"));
assetsBitmapData.push(bulkLoader.getBitmapData("background/4.png"));
init();
}
private final function init():void
{
masterContainer = new Sprite();
containers.push(genSquareBg());
containers[0].x = 0;
containers[0].y = 0;
masterContainer.addChild(containers[0]);
masterContainer.x = -(stageWidth / 2);
masterContainer.y = -(stageHeight / 2);
masterContainer.cacheAsBitmap = true;
addChild(masterContainer);
}
private static function genSquareBg():Sprite
{
var width:Number = stageWidth * 2;
var height:Number = stageHeight * 2;
var startX:Number = 0;
var startY:Number = 0;
var scale:Number;
var drawAmount:uint;
var tmpBitmap:Bitmap;
var i:uint;
var container:Sprite = new Sprite();
tmpBitmap = UtilLib.copyDataToBitmap(assetsBitmapData[0], false, 0x000000);
tmpBitmap.x = startX;
tmpBitmap.y = startY;
container.addChild(tmpBitmap);
drawAmount = UtilLib.getRandomInt(1, 2);
for(i = 1; i <= drawAmount; i++)
{
tmpBitmap = UtilLib.copyDataToBitmap(assetsBitmapData[1], true, 0x000000);
tmpBitmap.alpha = UtilLib.getRandomInt(3, 7) / 10;
tmpBitmap.rotation = UtilLib.getRandomInt(0, 360);
scale = UtilLib.getRandomInt(3, 10) / 10;
tmpBitmap.scaleX = scale; tmpBitmap.scaleY = scale;
tmpBitmap.x = UtilLib.getRandomInt(startX + tmpBitmap.width, width - tmpBitmap.width);
tmpBitmap.y = UtilLib.getRandomInt(startY + tmpBitmap.height, height - tmpBitmap.height);
container.addChild(tmpBitmap);
}
drawAmount = UtilLib.getRandomInt(2, 4);
for(i = 1; i <= drawAmount; i++)
{
tmpBitmap = UtilLib.copyDataToBitmap(assetsBitmapData[2], true, 0x000000);
tmpBitmap.alpha = UtilLib.getRandomInt(3, 10) / 10;
scale = UtilLib.getRandomInt(15, 40);
tmpBitmap.scaleX = scale / 10;
tmpBitmap.scaleY = UtilLib.getRandomInt(scale / 1.5, scale * 1.5) / 10;
tmpBitmap.x = UtilLib.getRandomInt(startX, width - tmpBitmap.width);
tmpBitmap.y = UtilLib.getRandomInt(startY, height - tmpBitmap.height);
container.addChild(tmpBitmap);
}
drawAmount = UtilLib.getRandomInt(0, 2);
for(i = 1; i <= drawAmount; i++)
{
tmpBitmap = UtilLib.copyDataToBitmap(assetsBitmapData[3], true, 0x000000);
tmpBitmap.alpha = UtilLib.getRandomInt(3, 10) / 10;
scale = UtilLib.getRandomInt(5, 40) / 10;
tmpBitmap.scaleX = scale; tmpBitmap.scaleY = scale;
tmpBitmap.x = UtilLib.getRandomInt(startX, width - tmpBitmap.width);
tmpBitmap.y = UtilLib.getRandomInt(startY, height - tmpBitmap.height);
container.addChild(tmpBitmap);
}
return container;
}
}
}
UtilLib.as copyDataToBitmap function:
public static function copyDataToBitmap(bitmapData:BitmapData, transparency:Boolean = false, flatBackground:uint = 0x000000):Bitmap
{
var width:Number = bitmapData.width;
var height:Number = bitmapData.height;
var tmpBitmapData:BitmapData = new BitmapData(width, height, transparency, flatBackground);
tmpBitmapData.copyPixels(bitmapData, new Rectangle(0, 0, width, height), new Point(0, 0));
return new Bitmap(tmpBitmapData);
}
The background images used are about 30 kilobytes, but some of them are very large:
alt text http://feedpostal.com/client/assets/background/1.gif
alt text http://feedpostal.com/client/assets/background/ 2.png
alt text http://feedpostal.com/client/assets/background/3.png
alt text http://feedpostal.com/client/assets/background/4.png
, , , . .
, Flex 3, 20 ( 1 ), , , , , 600 , 20 . , 8 64- , , ?
, . ( Photoshop).