va.lent.in — what a flash developer’s site should be

valentin

A couple of weeks ago I released my new personal portfolio/sandbox site — va.lent.in. I need more opinions and less bug reports.

I like projects which make me learn something. During this one I learned a lot…

Continue reading ‘va.lent.in — what a flash developer’s site should be’

Great image distortion idea

Recently I wrote about my distortion effect. Believe me, I experimented with it a lot. And it was damn slow on big bitmaps. But here’s a funny way to do it through native jpeg decoding. And it runs smooth on relatively big bitmaps. The guy messes up with jpeg data in ByteArray many times in a row loading it over and over again. Letting Flash native jpeg decoder do the work for him. This is is a great example of creative thinking!

The power of JSFL

When you want to automate boring repetitive actions in Flash IDE, JSFL is your best friend. It’s basically AS1 for working with FLA contents. Recently I had to dig deeper in JSFL and here are the results.

(0,0) and transformation point.

Flash animators made lots of character animations for a new game. All character’s parts will be replaced in game engine with skinned ones for different characters. But animation stays the same, so essentially an animation is a bunch of moving empty MovieClips where skins will be added later. The problem was that they used Flash IDE transformation point not in anchor (0,0) point. I couldn’t find any reference to that magic point in AS3 help, so people would have to move all parts in all animations and symbols in each part manually. That looks like a very time consuming process. So I opened JSFL help and wrote this:

function run() {
	var hash = {};

	var items = fl.getDocumentDOM().library.getSelectedItems();
	for ( var u = 0; u < items.length; u++ ) {
		var item = items[u];
		var layers = item.timeline.layers.length;
		for ( var i = 0; i < layers; i++ ) {
			var frames = item.timeline.layers[i].frames.length;
			for ( var j = 0; j < frames; j++ ) {
				var element = item.timeline.layers[i].frames[j].elements[0];
				if ( element && element.libraryItem != undefined ) {
					if ( !hash[element.libraryItem.name] ) {
						hash[element.libraryItem.name] = element.getTransformationPoint();
					}
					element.x = element.transformX;
					element.y = element.transformY;
					element.setTransformationPoint({x:0, y:0});
				}
			}
		}
	}

	for ( var i = 0; i < fl.getDocumentDOM().library.items.length; i++ ) {
		var item = fl.getDocumentDOM().library.items[i];
		if ( hash[item.name] ) {
			var timeline = item.timeline;
			for ( var l = 0; l < timeline.layers.length; l++ ) {
				for ( var m = 0; m < timeline.layers[l].frames[0].elements.length; m++ ) {
					var insideElement = timeline.layers[l].frames[0].elements[m];
					insideElement.x -= hash[item.name].x;
					insideElement.y -= hash[item.name].y;
				}
			}
			delete hash[item.name];
		}
	}

}

run();

The script moves all body parts in all selected library animations to make transformation point and anchor point match and after that moves body parts’ inner graphics accordingly. First, it creates a hash of body parts not to move their content several times resulting in broken animation. After, it iterates through selected library symbols, their layers and frames, adds all instances of library symbols to the hash, moves them to (transformX, transformY) and resets transformation points. The next loop just moves inner content in separate direction by the same distance.

Problem solved! Spend 30 minutes to read manuals and 20 minutes to write actual code but saved several days of animators’ work.

Add names to MovieClips in named layers.

The next task was to add instance names to empty MovieClips in animations. Thanks God all layers with body parts were named uniformly everywhere. Here’s the code:

function run() {
	var map = {	hand_R_anim: "p04",
				leg_R_anim: "p06",
				foot_R_anim: "p08",
				head_anim: "p01",
				body_anim: "p02",
				leg_L_anim: "p05",
				foot_L_anim: "p07",
				hand_L_anim: "p03"
			};

	var items = fl.getDocumentDOM().library.getSelectedItems();
	for ( var u = 0; u < items.length; u++ ) {
		var item = items[u];
		var timeline = item.timeline;
		var layers = timeline.layers;
		for ( var i = 0; i < layers.length; i++ ) {
			var layer = layers[i];
			var name = map[layer.name];
			if ( name ) {
				for ( var j = 0; j < layer.frames.length; j++ ) {
					var element = layer.frames[j].elements[0];
					if ( element ) {
						element.name = name;
					}
				}
			}
		}
	}
}

run();

Split legs and feet.

And the last task was to split legs and feet animations because they should have been separate body parts but were drawn and animated as one MovieClip everywhere.

function run() {
	var path = "1/anim/";

	var items = fl.getDocumentDOM().library.getSelectedItems();
	for ( var u = 0; u < items.length; u++ ) {
		var item = items[u];
		var timeline = item.timeline;
		var layers = timeline.layers;
		var layerR = 0;
		var layerL = 0

		for ( var i = 0; i < layers.length; i++ ) {
			if ( layers[i].name == "foot_R_anim" ) layerR = i;
		}
		timeline.setSelectedLayers(layerR);
		timeline.copyFrames(0, layers[layerR].frameCount);
		var created = timeline.addNewLayer( "leg_R_anim" );
		timeline.setSelectedLayers(created);
		timeline.pasteFrames();
		for ( var i = 0; i < layers[created].frameCount; i++ ) {
			var element = layers[created].frames[i].elements[0];
			if ( element && element.libraryItem.name == path + "foot_R_anim" ) {
				element.libraryItem = fl.getDocumentDOM().library.items[fl.getDocumentDOM().library.findItemIndex(path + "leg_R_anim")];
			}
		}

		layers = timeline.layers;
		for ( var i = 0; i < layers.length; i++ ) {
			if ( layers[i].name == "foot_L_anim" ) layerL = i;
		}
		timeline.setSelectedLayers(layerL);
		timeline.copyFrames(0, layers[layerL].frameCount);
		var created = timeline.addNewLayer( "leg_L_anim" );
		timeline.setSelectedLayers(created);
		timeline.pasteFrames();
		for ( var i = 0; i < layers[created].frameCount; i++ ) {
			var element = layers[created].frames[i].elements[0];
			if ( element && element.libraryItem.name == path + "foot_L_anim" ) {
				element.libraryItem = fl.getDocumentDOM().library.items[fl.getDocumentDOM().library.findItemIndex(path + "leg_L_anim")];
			}
		}
	}
}

run();

Here we once again take selected items in library. Path variable is the folder with body parts. First we look for the needed layer number, select frames, copy and paste them to a new layer. After that we iterate through frames and existing instances to be of another symbols. We do it 2 times for both legs.

Good AS2 programmers are so rare these days

Who would expect it to be so hard to find a good ActionScript 2 (yes, ActionScript TWO) developer these days. Our company’s main project is a big flash MMO written in AS2 long time ago. It is still popular and brings a lot of money. But people come and go, so live team department is looking for an AS2 programmer without success so far.

There’re just no AS2 developers left. And that’s obvious why. You either go mainstream (if you are decent) and learn AS3 or work for a company coding banners in frame scripts (if you are bad or don’t want to learn). Nobody stays long (if at all) in AS2 world these days. As it has been so far, people who claim to know AS2 are just bad. They don’t know AS2, they know just a bit of programming. No OOP or design patterns. I’m not saying that AS2 can be tricky sometimes and you have to be good to support a large-scale project.

Personally, I should not mention AS2 knowledge in my resume because I totally forgot it. And don’t really want to return back to those days. But if offered 2x payment raise I’d think about it.

Parsing PDFs

Why do people keep making libraries which can only CREATE pdf files? I want one which can parse pdfs and export text/images with layout.

MXML in AS3 projects and fixing mxmlc

Not many people actually know that you can use MXML in pure AS3 projects. Here’s an example.

Create a new AS3 project, create a new file in package cp. Call it App.mxml. Flex Builder will not allow you to create an MXML component in a pure AS3 project, so you have to do it manually. Here’s my App.mxml.

<?xml version="1.0" encoding="utf-8"?>
<cp:Component xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:cp="cp.*" >
    <cp:Component var1="123"/>
    <cp:Component var1="124111"/>
    <cp:Component />
</cp:Component>

Notice that I’m using my own namespace and no flex components. Now define Component class in cp/Component.as.

package cp {
import flash.display.Sprite;

[DefaultProperty( "children" )]
public class Component extends Sprite {
    public function Component() {
        super();
    }

    private var _children:Array;
    public function get children():Array {
        return this._children;
    }
    public function set children(value:Array):void {
        this._children = value;
        trace( value );
    }

    public var var1:String;
}
}

In main project file I put:

package {
import cp.App;
import flash.display.Sprite;

public class mxml_test extends Sprite {
    public function mxml_test()
    {
        this.addChild( new App() );
    }
}
}

To compile it you need to have framework.swc in library path though. And [DefaultProperty( "children" )] doesn’t work neither in 3.4 SDK nor in 4.0 for some reason. People say it should work in 4.0 but it doesn’t.

That’s weird that Flex SDK 3.4 and Flex SDK 4.0 produce different code from MXML files. 4.0 adds much more useless Flex stuff to resulting SWF. If you want you can add -keep-generated-actionscript compiler parameter to check converted code. Flex 4.0 adds such functions for every object in original MXML.

private function _App_Component2_i() : cp.Component {
    var temp : cp.Component = new cp.Component();
    temp.var1 = "123";
    _App_Component2 = temp;
    mx.binding.BindingManager.executeBindings(this, "_App_Component2", _App_Component2);
    return temp;
}

I don’t like that executeBindings thing in my code and useless mx.* classes it puts in SWF making it 3.5Kb in size. So I decided to patch Flex SDK since it’s kind of open source. Flex SDK SVN can be found at opensource.adobe.com/svn/opensource/flex/ and it contains everything you should need to rebuild your own version of SDK. These links helped me a lot too, and thanks God I found those Eclipse projects inside after trying to resolve libraries issues for an hour.

There are only 2 places where I found this executeBindings stuff, both classes are in flex2.compiler.mxml.gen package. I commented this code and compiled mxmlc.jar. I’ve been doing it all evening but couldn’t get rid of it. I can’t get why. I am totally sure that new mxmlc.jar is used but this stupid string is there anyway.

I consider the final result to be a failure but at least I now know that you can patch Flex SDK as you wish. But I didn’t have time to try to understand how the whole compiler code works and where it handles bindings exactly. I didn’t find much on the internet so I guess not many people actually tried to build their own versions of compiler.

How iPad affects flash developers.

So, earlier this week Apple announced iPad.

ipad

Everyone and their mom has already blogged about it, but my view is kind of different. Considering other blog posts I’ve read so far.

Is iPad a failure?

First of all, people were waiting for a MacBook Touch but got a giant iPod. Second, the majority of bloggers are IT specialists or geeks. iPad isn’t a MacBook Touch and isn’t really for geeks –> iPad == failure. That’s the logic I see everywhere.

I was thinking same way until I tried to look at the device from a common user’s view. Let’s take my mom. iPad is actually how she sees a computer. It’s easy to use, fast, can browse internet for news and play videos. And it’s cheap. iPad is perfect for her. I’m seriously thinking to buy her one when it comes out.

iPad is a tablet PC for common people not for geeks like us.

So, no, iPad is not a failure.

What if Apple released something like a MacBook Touch?

Well, what do we have now (actually, will have in 2 months). A fast, easy to use device with big cool multi-touch screen. App store offers a lot of software and soon there will be a lot of apps adapted for large screen. It can work for 10 hours straight. Millions of iPhone/iPod owners know how to use it. And it’s cheap.

Now imagine a MacBook Touch.

  1. It’s damn expensive,
  2. It can work for 2 hours without recharge,
  3. It’s kind of slow and everything is too small for a touch interface,
  4. There are no apps for it, existing OS X software is unusable with touch screen,
  5. Common users don’t really know how to use it.

Apple tries to hold everything in their hands. That’s why general non-geek users will love the ease of use and good usability. I predict it to be very popular.

Unlike other tablets with Windows 7 trying to be small laptops without keyboards. I’m sure a lot of geeks will buy those, but geeks are a small percent of people. Do you think why iPhone is so popular? It’s a closed platform too. Unlike Android which is a geeks-2-geeks product.

So, how iPad affects flash developers.

Personally, I pray for CS5 to be able to publish real iPad applications. Adjusted for big screen, working fast and smooth. Otherwise I’ll have to learn Objective-C. iPad will definitely be a trend which you can’t just ignore. Apple vs. Adobe war makes us flash developers suffer according to theflashblog.

ipad2

I understand why Apple is doing this but nowadays web experience without Flash isn’t full. Flash is everywhere. If it wasn’t a big problem with iPhone, it definitely will be with iPad. It is targeted for common users and they don’t care why sites can’t be shown as they are used to. Hope Apple and Adobe will come to an agreement some day.

As for now flash developers are not needed for iPad because there’s no flash. If mobile Safari supports HTML5 and the power of AJAX there will be more sites using these technologies instead of Flash.

But as I already said, if CS5 is able to create full-featured iPad apps it will be TOTALLY AWESOME! Flash developers will rock the world!

Delete .svn folders

Just so I won’t forget.

find ./ -name ".svn" | xargs rm -Rf

OMG! as3-signals are faster than events!!!!!1

Continuing on as3-signals. Here the guy actually ran performance tests on as3-signals vs. events. And got surprisingly SHOCKING results. You won’t gonna believe — AS3-SIGNALS ARE FASTER THAN EVENTS!!!!!1 Who could have thought that, huh? OK, </sarcasm>. Just looking at the code you can tell that as3-signals being simpler will be faster. This is definitely good.

But, if you need 0(zero) or 1(one) listener for your object, use plain old callbacks and you will be shocked by performance boost!

I added this code to the guy’s test:

public var callback:Function;
public function dataCallback():void
{
for (var i:uint = 0; i < loops; i++)
this.callback(i);
}

And here are the results

––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
1 (5 iterations)
Player version: MAC 10,0,42,34 (debug)
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
method...................................................ttl ms...avg ms
Events                                                     3107   621.40
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––

––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
1 (5 iterations)
Player version: MAC 10,0,42,34 (debug)
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
method...................................................ttl ms...avg ms
Signals                                                    2198   439.60
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––

––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
1 (5 iterations)
Player version: MAC 10,0,42,34 (debug)
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
method...................................................ttl ms...avg ms
Callback                                                    255    51.00
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––

Someone has too much time running tests and drawing graphs. Oh wait, it’s me. At least I haven’t drawn a graph.

P.S. Once again, as3-signals is good, opensource is good! cheers~

If Unity3D can why Flash can’t?

Most of you must have seen examples of Unity3d, like this one for example. On my MBP it runs smoothly in Firefox (people say that it’s total disaster on Safari though).

unity

But this is just one example of what is possible in the browser.

So, why Flash can’t do that? I bet Adobe can release a new player with all cool features we’ve ever wanted right now. But here come business interests. They have CS4 and CS5 to sell. Most likely we will get some of those cool things in CS6 and hardware 3D in like cs42. It’s business, guys. Live with it.

How I made the “TV glitch effect” for va.lent.in

You should have seen the effect of a TV signal glitch on text at my last va.lent.in site. People keep asking me how it was done over and over again. I won’t post complete source code here because it’s kind of messy. I’ll just give basic ideas, because this effect is rather simple.

Basically, you just displace red, green and blue channels of a bitmap separately and apply the same actions to resulting bitmap. Adding some kind of wave function looks cool too, but is damn slow. So, what I decided to do is to distort a small rectangle of target image over a couple of frames. First, I used only BitmapDatas but later tried to test PixelBender performance. Honestly, I don’t remember any miraculous speed gain, so there might be none at all. But anyway, here’s the code of this filter. Basically, it’s just 3 lines of code:

void evaluatePixel()
{
    float2 outCoords = outCoord();
    dst.r = sample( src, float2(outCoords.x + rOffset, outCoords.y) ).r;
    dst.g = sample( src, float2(outCoords.x + gOffset, outCoords.y) ).g;
    dst.b = sample( src, float2(outCoords.x + bOffset, outCoords.y) ).b;
}

Full source code you can download here: RGBDisplacement.pbk

This shader takes 3 displacement values as parameters. Now back to AS3. Once again, this code is result of numerous experiments, changes and fixes during development. Please don’t use it as is (actually it doesn’t work alone) but rather try to understand the basic idea.

[Embed(source=&quot;filters/RGBDisplacement.pbj&quot;, mimeType=&quot;application/octet-stream&quot;)]
 private var ShaderDistort:Class;

 public function Distortion( source: Sprite, minAmp: int = 10, maxAmp: int = 40, len: int = 30, minRand: Number = .05, ampRand: Number = .8, maxH: int = 20 )
 { ... }

Shader class and constructor params.

public function start( interval: int = -1 ): void
{
    if ( bitmapData.width != Math.ceil(_source.width) || bitmapData.height != Math.ceil(_source.height) )
    {
        initBitmaps();
    }

    seed = 0;
    _source.alpha = 0;
    visible = true;
    this.interval = interval;
    // set random displacement
    bmpY = Math.random()*(bitmapData.height-bitmapDataDistorted.height);

    bitmapData.fillRect( bitmapData.rect, 0x000000 );
    bitmapData.draw( _source );
    business();
 }

This method resets distortion params, sets bmpY to a random starting y position and invokes business() which does the job.

private function business( evt: Event = null ): void
{
    // stop if played for certain number of frames
    if (interval == 0)
    {
        stop();
        return;
    }
    interval--;

    // randomly move the effect up or down
    bmpY += int((10 - Math.random()*20) * Math.sqrt(interval));
    bmpY &lt; 0 ? bmpY = 0 : true;
    bmpY &gt; bitmapData.height-bitmapDataDistorted.height ? bmpY = bitmapData.height-bitmapDataDistorted.height : true;

    var bmpd: BitmapData = new BitmapData( bitmapDataDistorted.width, bitmapDataDistorted.height, false, 0x000000 );

    bmpd.copyPixels( bitmapData, new Rectangle(0, bmpY, bitmapDataDistorted.width, bitmapDataDistorted.height ), new Point(0, 0) );
    filter.data.src.input = bmpd;

    seed++;
    seed &gt;= len ? seed = 0: true;

    // wave function
    var v: Number = 3.14159265 / len * seed;
    // approximation of sin()
    var sin: Number = 1.27323954 * v - 0.405284735 * v * v;
    var amp: Number = minAmp + sin * difAmp;

    var i: int = 0;
    var disp: Array = [];
    var pi2: Number = 6.28318531;

    for (i; i&lt;3; i++)
    {
        if ( seeds[i] &gt;= 1 )
        {
            seeds[i]--;
            dSeeds[i] = Math.random() * ampRand + minRand;
        }
        seeds[i] += dSeeds[i];
        v = seeds[i] * pi2;
        // approximation of sin()
        if (v &gt; 3.14159265) v -= 6.28318531;
        if (v &lt; 0)
            sin = 1.27323954 * v + .405284735 * v * v;
        else
            sin = 1.27323954 * v - 0.405284735 * v * v;
        disp[i] = sin * amp;
    }

    // set displacements for shader
    filter.data.rOffset.value = [disp[0]];
    filter.data.gOffset.value = [disp[1]];
    filter.data.bOffset.value = [disp[2]];

    // invoke shader
    var job: ShaderJob = new ShaderJob(filter, bitmapDataDistorted);
     job.addEventListener(ShaderEvent.COMPLETE, redraw);
    job.start();
}

And finally redraw() method

private function redraw( evt: Event = null ): void
{
     bitmapData.copyPixels( bitmapDataDistorted, bitmapDataDistorted.rect, new Point(0, bmpY) );
     id = setTimeout(business, 40);
}

That’s it. You can refer to my post about using Maple for more math.

The source of this class is here: Distortion.as