2011-06-15

Flex Tiling Image Class

So I needed a class that could be given a source image and would tile that source seamlessly as it is resized, but could also have the tiling offsets edited. I couldn't find any useful posts online about this, which could speak to my poor internetting skillz. (i don't think the "z" in skillz is merited when you suck at it...) So I built one and thought I'd share it in case any of you need one.

<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" >
<fx:Script>
<![CDATA[
private var _tileSource : BitmapData;
private var _offsetX : Number = 0;
private var _offsetY : Number = 0;
public function set tileSource(value : Object) : void {
bitImg.source = value;
var temp : * = new bitImg.source();
// Can't type it because Flex has a hissy fit
_tileSource = temp['bitmapData'];
}
public function setOffset(offsetX : Number, offsetY : Number) : void {
var degreeOfChange : Number = Math.abs(offsetX - _offsetX) + Math.abs(offsetY - _offsetY);
_offsetX = offsetX;
_offsetY = offsetY;
draw();
}
public function draw() : void {
bitImg.source = new BitmapData(width, height, true, 0x000000);
var drawPosX : Number = _offsetX;
drawPosX = new NumberRange(0 - _tileSource.width, 0).getLoopedValue(drawPosX);
var drawPosY : Number = _offsetY;
drawPosY = new NumberRange(0 - _tileSource.height, 0).getLoopedValue(drawPosY);
var startY : Number = drawPosY;
var tileRect : Rectangle = new Rectangle(0, 0, _tileSource.width, _tileSource.height);
while (drawPosX < width) {
while (drawPosY < height) {
bitImg.source.copyPixels(_tileSource, tileRect, new Point(drawPosX, drawPosY));
drawPosY += _tileSource.height;
}
drawPosX += _tileSource.width;
drawPosY = startY;
}
invalidateDisplayList();
}
override protected function updateDisplayList(unscaledWidth : Number, unscaledHeight : Number) : void {
super.updateDisplayList(unscaledWidth, unscaledHeight);

if (_tileSource) {
draw();
}
}
]]>
</fx:Script>
<s:BitmapImage id="bitImg" width="100%" height="100%"/>
</s:Group>
Oh, this uses my custom NumberRange class, so you get that too:

package framCommons {
/**
* @author Jellyfish Emperor
*/
public class NumberRange {
public var min : Number;
public var max : Number;
public function NumberRange(pMin:Number = 0, pMax:Number = 1) {
min = pMin;
max = pMax;
}

public function percent(value : Number) : Number {
return (value - min)/range;
}
public function get range():Number{
return max - min + 1;
}
public function get difference():Number{
return max - min;
}
public function random():Number{
return Math.random() * range + min;
}

public function getLoopedValue(i : Number) : Number {
var value:Number = i;
while(value < min){
value += range;
}
while(value > max){
value -= range;
}
return value;
}
}
}
 You can use it in MXML like so:

<fram:TilingImage id="midBg" tileSource="@Embed('/assets/images/mid.png')" width="100%" height="100%" />
And it will automatically update every time it is resized to fit perfectly. This is done because it overrides updateDisplayList and triggers a draw().

The reason I created this was to make easily parallaxing background. You can parallax like so, by calling setOffset every frame: (in this example, "level" would be the container of my foreground elements, that moves it's x,y to keep the player centred) notice that farther away layers offset less and less, to create the illusion of depth.
farBg.setOffset(level.x /6, level.y / 6); 
midBg.setOffset(level.x /4, level.y / 4); 
closeBg.setOffset(level.x /2, level.y / 2);


This is pretty rudimentary, but hopefully this helps some people out!

No comments:

Post a Comment