Monthly Archives: January 2009

Game of Life Snippets

SNIPPET ONE

SNIPPET TWO

I was very impressed when I saw Mario Klingemann's game of line in 3 lines of code blog post late last year. I've been meaning to post about it for awhile - finally got around to it.

Mario Klingemann is the developer of Peacock... an excellent node based image generator written in ActionScript.

BitmapData.paletteMap() is very powerful. I haven't done much with it, but I have some ideas floating around.

For those of you that are game of live savvy... try other rule sets from the lexicon... my personal favorite has always been coral (45678/3). Here's a link to some more.

Another short game of life recently appeared on on the 25lines.com forums.... here... the forum post features a nice explanation for using a ConvolutionFilter and Threshold to create The Game of Life. The technique is so small and impressive looking that I figured I'd post it as is right here:

Actionscript:
  1. addChild(new Bitmap(new BitmapData(stage.stageWidth, stage.stageHeight, false, 0)))["bitmapData"].noise(int(Math.random()*int.MAX_VALUE),0,255,7,true);
  2. addEventListener(Event.ENTER_FRAME, function (e) {
  3.    getChildAt(0)["bitmapData"].applyFilter(getChildAt(0)["bitmapData"], getChildAt(0)["bitmapData"].rect, new Point(), new ConvolutionFilter(3, 3, [3,3,3,3,2,3,3,3,3],255,0,true,false,0,1));
  4.    getChildAt(0)["bitmapData"].threshold(getChildAt(0)["bitmapData"], getChildAt(0)["bitmapData"].rect, new Point(), "==", 8, 0xFFFFFFFF, 0xFC);
  5.    getChildAt(0)["bitmapData"].threshold(getChildAt(0)["bitmapData"], getChildAt(0)["bitmapData"].rect, new Point(), "!=", 0xFFFFFFFF, 0x00000000)})

The above code is by Daniil Tutubalin 25lines.com forums.

UPDATE:

Since writing this post the 25lines.com forum thread has grown to include a 4 line version and some additional discussion about this topic... be sure to check it out.

Posted in BitmapData, misc, pixel manipulation | Tagged , | Leave a comment

Parallax Displacement Map w/BlendMode

Actionscript:
  1. var blends:Array = [BlendMode.ADD, BlendMode.DARKEN, BlendMode.DIFFERENCE, BlendMode.HARDLIGHT, BlendMode.INVERT, BlendMode.LIGHTEN, BlendMode.MULTIPLY, BlendMode.OVERLAY, BlendMode.SCREEN, BlendMode.SUBTRACT];

and a little later...

Actionscript:
  1. displace.perlinNoise(150,150, 3, 30, true, false,0,true);
  2. var currentBlend:String = blends[ blendCount % blends.length];
  3. displace.draw(radial, null ,null, currentBlend);
  4. blendCount++;

The above are excepts from a recommendation I made in the comments of yesterdays post...


Try some different blend modes.... take a look at the swf here.

Posted in 3D, BitmapData, misc | Tagged , | Leave a comment

Parallax Displacement Map

Actionscript:
  1. [SWF(width=500, height=500, backgroundColor=0xCCCCCC, frameRate=30)]
  2.  
  3. var canvas:BitmapData = new BitmapData(500, 500, true, 0xFF000000);
  4. addChild(new Bitmap(canvas));
  5.  
  6. // create a radial gradient
  7. var radial:Shape = new Shape();
  8. var m:Matrix = new Matrix()
  9. m.createGradientBox(500,500,0,0,0);
  10. radial.graphics.beginGradientFill(GradientType.RADIAL, [0xFFFFFF, 0x000000], [1, 1], [0, 200], m, SpreadMethod.PAD);
  11.  
  12. radial.graphics.drawRect(0,0,500,500);
  13. radial.x = radial.y = 0;
  14.  
  15. var displace:BitmapData = new BitmapData(500, 500, false,0xFF000000);
  16. displace.perlinNoise(150,150, 3, 30, true, false,0,true);
  17.  
  18. // try different blendmodes here
  19. displace.draw(radial, null ,null, BlendMode.LIGHTEN);
  20.  
  21. var displacementMap:DisplacementMapFilter = new DisplacementMapFilter(displace, new Point(0,0), 1, 1,  0, 0, DisplacementMapFilterMode.WRAP);
  22.  
  23. var scale:Number = 50 / stage.stageWidth;
  24.  
  25. addEventListener(Event.ENTER_FRAME, onLoop);
  26. function onLoop(evt:Event):void {
  27.  
  28.     canvas.copyPixels(displace, canvas.rect, new Point(0,0));
  29.      
  30.     displacementMap.scaleX = 25 - mouseX  * scale ;
  31.     displacementMap.scaleY = 25 - mouseY  * scale ;
  32.    
  33.     canvas.applyFilter(canvas, canvas.rect, new Point(0,0), displacementMap);
  34. }

This one is hard to describe, so take a look at the swf first.

I think displacement maps are underused - they're very powerful. In this snippet I use a DisplacementMapFilter to create a parallax effect on some perlin noise and a radial gradient. The result is an abstract texture that looks 3D.

I got the idea from a demo for the alternative game engine. The first time I saw this demo I had no idea how they were doing it... but after looking at it a few times, I noticed some distortion around the neck area of the model... at that point I recognized it as a displacement map and whipped up a demo (using a drawings as the source image). The alternativa demo also has some color stuff happening to simulate lighting... I'm assuming that's done with a ColorMatrixFilter or two, but I'm not sure.

As in the alternativa demo, this technique could be used with 2 images... one depth map rendered out from your favorite 3D app and one textured render....

Posted in 3D, BitmapData, misc | Tagged , | Leave a comment

Matrix() 3 Point Skew

Actionscript:
  1. [SWF(width=500, height=500, backgroundColor=0xFFFFFF, frameRate=30)]
  2.  
  3. var box:Sprite = createSprite("Rect", 0, 0, 100, 100, 0xFF0000);
  4.  
  5. var d0:Sprite = drag(createSprite("Ellipse",  -5, -5, 10, 10));
  6. d0.x = d0.y = 200;
  7.  
  8. var d1:Sprite = drag(createSprite("Ellipse",  -5, -5, 10, 10));
  9. d1.x = 200
  10. d1.y = 300;
  11.  
  12. var d2:Sprite = drag(createSprite("Ellipse",  -5, -5, 10, 10));
  13. d2.y = d0.y;
  14. d2.x = 300;
  15.  
  16. addEventListener(Event.ENTER_FRAME, onLoop);
  17.  
  18. function onLoop(evt:Event):void {
  19.  
  20.     var m:Matrix = new Matrix();
  21.     m.scale((d2.x - d0.x) / 100,(d1.y - d0.y) / 100);
  22.     m.translate(d0.x, d0.y);
  23.    
  24.     m.c =  (d1.x - d0.x) / 100
  25.     m.b =  (d2.y - d0.y ) / 100
  26.    
  27.     box.transform.matrix = m;
  28. }
  29.  
  30. function createSprite(shape:String, xp:Number, yp:Number, w:Number, h:Number, col:uint=0x444444):Sprite {
  31.     var s:Sprite = new Sprite();
  32.     s.graphics.beginFill(col);
  33.     s.graphics["draw" + shape](xp, yp, w, h);
  34.     addChild(s);
  35.     return s;
  36. }
  37.  
  38. function drag(target:*):*{
  39.     target.addEventListener(MouseEvent.MOUSE_DOWN, function(evt:MouseEvent){ evt.currentTarget.startDrag(); });
  40.     return target;
  41. }
  42.  
  43. stage.addEventListener(MouseEvent.MOUSE_UP, function(){ stopDrag() });

The above creates a red rectangle that can be skewed by dragging 3 points. Why is this so cool you ask?

This is why (move your mouse)

The above was written in flash 7, before the Matrix object existed. So in order to create it I used this technique from Eric Lin.

Posted in DisplayObject, MovieClip, matrix | Tagged , | Leave a comment

nesting(function(calls()))

Actionscript:
  1. [SWF(width=500, height=500, backgroundColor=0x000000, frameRate=30)]
  2.  
  3. for (var i:int = 0; i<10; i++){
  4.        // draggable ellipse
  5.     var dot:Sprite = drag(createSprite("Ellipse",  -10, -10, 20, 20));
  6.     dot.x = Math.random() * stage.stageWidth ;
  7.     dot.y = Math.random() * stage.stageHeight ;
  8. }
  9.  
  10. for (i = 0; i<10; i++){
  11.  
  12.       var box:Sprite = drag(spin(createSprite("Rect", -20, -20, 40, 40, 0xFF0000), Math.random()*5 + 1));
  13.     box.x = Math.random() * stage.stageWidth ;
  14.     box.y = Math.random() * stage.stageHeight ;
  15. }
  16.  
  17.  
  18.  // createSprite can create ellipses or rectangles
  19. function createSprite(shape:String, xp:Number, yp:Number, w:Number, h:Number, col:uint=0x444444):Sprite {
  20.     var s:Sprite = new Sprite();
  21.     s.graphics.beginFill(col);
  22.     // trick from a previous post
  23.     s.graphics["draw" + shape](xp, yp, w, h);
  24.     addChild(s);
  25.     return s;
  26. }
  27.  
  28. // drag and spin add listeners to an untyped target and return that target for easy function nesting
  29. function drag(target:*):*{
  30.     target.addEventListener(MouseEvent.MOUSE_DOWN, function(evt:MouseEvent){ evt.currentTarget.startDrag(); });
  31.     return target;
  32. }
  33.  
  34. function spin(target:*, speed:Number):*{
  35.     target.addEventListener(Event.ENTER_FRAME, function(evt:Event){ evt.currentTarget.rotation+=speed; });
  36.     return target;
  37. }
  38.  
  39. stage.addEventListener(MouseEvent.MOUSE_UP, function(){ stopDrag() });

The above will create some draggable circles and some rotating draggable rects... but that's not really the point....

When prototyping and just playing around I write functions that take an Object as an argument, alter that Object in some way and pass that some Object out as a return value.... this makes it so I can write things like this:

Actionscript:
  1. drag(spin(createSprite("Rect", -20, -20, 40, 40, 0xFF0000), Math.random()*5 + 1));

Readability can be a problem so... consider that before using this for anything...

Posted in dynamic, functions, misc | Tagged , | Leave a comment

Image Airbrush

Actionscript:
  1. [SWF(width=500, height=500, backgroundColor=0x000000, frameRate=30)]
  2.  
  3. // draw an ugly gradient
  4. var size:int = 500;
  5. var pixNum:int = size * size;
  6. var gradient:BitmapData = new BitmapData(size, size, true, 0xFF000000);
  7.  
  8. gradient.lock();
  9. var xp:int, yp:int;
  10. for (var i:int = 0; i<pixNum; i++){
  11.     xp = i % size;
  12.     yp = i / size;
  13.     gradient.setPixel(xp, yp, (yp /= 2) <<16 | (255 - yp) <<8 | (xp / 2) );
  14. }
  15. gradient.unlock();
  16.  
  17. // draw an alphaChannel (radial gradient)
  18. var radius:Number = 50;
  19. var diameter:Number = radius * 2;
  20.  
  21. pixNum = diameter * diameter;
  22.  
  23. var brushAlpha = new BitmapData(diameter, diameter, true, 0x00000000);
  24. var dx:int, dy:int;
  25. var ratio:Number = 255 / radius;
  26. var a:int;
  27.  
  28. brushAlpha.lock();
  29. for (i = 0; i<pixNum; i++){
  30.     xp = i % diameter;
  31.     yp = i / diameter;
  32.     dx = xp - radius;
  33.     dy = yp - radius;
  34.     a = int(255 - Math.min(255,Math.sqrt(dx * dx + dy * dy) * ratio));
  35.     brushAlpha.setPixel32(xp, yp, a <<24);
  36. }
  37. brushAlpha.unlock();
  38.  
  39. // create a black canvas
  40. var canvas:BitmapData = new BitmapData(size, size, true, 0xFF000000);
  41. addChild(new Bitmap(canvas));
  42.  
  43. addEventListener(Event.ENTER_FRAME, onLoop);
  44. function onLoop(evt:Event):void {
  45.     // draw the gradient onto the canvas using the alphaChannel (brushAlpha);
  46.     xp = mouseX - radius;
  47.     yp = mouseY - radius;
  48.     canvas.copyPixels(gradient,
  49.                     new Rectangle(xp, yp, diameter, diameter),
  50.                     new Point(xp, yp), brushAlpha, new Point(0,0), true);
  51. }

This demo creates an airbrush that paints one BitmapData onto another. This is achieved by using the alpha related arguments of the BitmapData.copyPixels() function. If your not familiar with these you should take some time to play around with them... they're very powerful.

copyPixels() is the fastest way to draw in flash, so if you need speed, copyPixels() is the way to go. It's much faster than using draw().

Posted in BitmapData, setPixel | Tagged , | 2 Comments

Script List Pattern

Actionscript:
  1. var currentState:String = "";
  2.  
  3. var functionList:Vector.<Function> = new Vector.<Function>();
  4.  
  5. function clearFunctions():void{
  6.     functionList = new Vector.<Function>();
  7. }
  8. function addFunction(f:Function):Function {
  9.     functionList.push(f);
  10.     return addFunction;
  11. }
  12.  
  13. function removeFunction(f:Function):void {
  14.     for (var i:int = 0 ; i<functionList.length; i++){
  15.         if (f == functionList[i]){
  16.             functionList.splice(i, 1);
  17.         }
  18.     }
  19. }
  20.  
  21. function runProgram():void {
  22.    
  23.     currentState = "current: ";
  24.    
  25.     for (var i:int = 0; i<functionList.length; i++){
  26.         functionList[i]();
  27.     }
  28.    
  29.     trace(currentState);
  30. }
  31.  
  32. function one():void{
  33.     currentState += " one";
  34. }
  35.  
  36. function two():void {
  37.     currentState += " two";
  38. }
  39.  
  40. function three():void {
  41.     currentState += " three";
  42. }
  43.  
  44. function dot():void{
  45.     currentState += ".";
  46.    
  47. }
  48.  
  49. // test it:
  50. addFunction(one);
  51. addFunction(two);
  52. addFunction(three);
  53.  
  54. runProgram();
  55.  
  56. removeFunction(one);
  57.  
  58. runProgram();
  59.  
  60. addFunction(dot)(dot)(dot);
  61.  
  62. runProgram();
  63.  
  64. clearFunctions();
  65.  
  66. addFunction(dot)(dot)(dot);
  67.  
  68. addFunction(three)(two)(one)(dot)(dot)(dot);
  69.  
  70. runProgram();
  71.  
  72. /* outputs:
  73. current:  one two three
  74. current:  two three
  75. current:  two three...
  76. current: ... three two one...
  77. */

This is a very quick implementation of a pattern that I use sometimes. The idea of this pattern is very simple and can easily be implemented in OOP or procedural style programming. The idea is to have a Vector/Array of functions or Class instances. Loop through this Vector/Array and run each function (or a given method of each Class instance). During runtime your client code can alter this list to change what the program does.

I use this technique for games quite often. All enemies get added to an enemy list - this list is looped through and each enemies run() method is called. If an enemy dies it dispatches an event that tells the enemy manager to remove it from the list. Some pseudo code:

Actionscript:
  1. function onMainLoop():void{
  2.     if (!paused){
  3.        
  4.         runWorld();
  5.         runKeys();
  6.         runChar();
  7.        
  8.         enemyManager.runEnemies();
  9.        
  10.         runPickups();
  11.        
  12.     }else{
  13.         // show pause screen
  14.     }
  15. }
  16.  
  17. //... inside EnemyManager class
  18. function onRunEnemies():void{
  19.     for (var i:int = 0; i<enemyList.length; i++){
  20.             enemyList[i].run(i);
  21.     }
  22. }

I use the same technique for pickups (coins, lives etc....).

I first used this technique in Director with a list of parent scripts.

I'm aware of other more refined patterns that are meant to do similar things, but for small to medium sized apps this has worked very nicely for me.

Posted in Vector, arrays, misc | Tagged , | 2 Comments

BitmapData lock() & unlock()

Actionscript:
  1. var size:Number = 400;
  2. var pixelNum:Number = size * size;
  3. var pixels:Vector.<uint> = new Vector.<uint>();
  4. var r:uint;
  5. var g:uint;
  6. var b:uint;
  7.  
  8. var canvas:BitmapData = new BitmapData(size,size,false,0x000000);
  9. addChild(new Bitmap(canvas));
  10.  
  11. addEventListener(Event.ENTER_FRAME, onLoop);
  12. function onLoop(evt:Event):void {
  13.  
  14.     canvas.lock();
  15.  
  16.     // do any kind of pixel manipulation here:
  17.    
  18.     for (var i:int = 0; i<pixelNum; i++) {
  19.         // draw a gradient that changes based on
  20.         // mouse position
  21.         r = i % size + mouseX;
  22.         b = i / size + mouseY;
  23.         g = (r + b) % mouseX;
  24.         pixels[i] = r <<16 | g <<8 | b;
  25.     }
  26.     canvas.setVector(canvas.rect, pixels);
  27.     //
  28.  
  29.     canvas.unlock();
  30. }

Using BitmapData.lock() and BitmapData.unlock() can speed up BitmaData drawing operations significantly. Simply lock your BitmapData before you draw to it, and unlock it when your done.

I often forget to do this until I get to the optimize phase of a project... but I think I'd like to get in the habit of doing it all the time....

Posted in BitmapData, pixel manipulation | Tagged , | 2 Comments

BitmapData Trails

Actionscript:
  1. [SWF(width=400, height=400, backgroundColor=0xCCCCCC, frameRate=30)]
  2.  
  3. var canvas:BitmapData = new BitmapData(400, 400, true, 0xCCCCCC);
  4. var eraser:BitmapData = new BitmapData(400, 400, true, 0x22CCCCCC);
  5. addChild(new Bitmap(canvas));
  6.  
  7. var circle:Shape = Shape(addChild(new Shape()));
  8. with (circle.graphics) beginFill(0x000000), drawCircle(0,0,20);
  9.  
  10. addEventListener(Event.ENTER_FRAME, onLoop);
  11.  
  12. function onLoop(evt:Event):void {
  13.     canvas.copyPixels(eraser, eraser.rect, new Point(0,0), null, null, true);
  14.     circle.x = mouseX;
  15.     circle.y = mouseY;
  16.    
  17.     canvas.draw(circle, circle.transform.matrix);
  18. }

Create trails by slowly erasing the background with copyPixels(). The first time I ever saw this technique was back when setpixel.com contained a bunch of great director experiments by Charles Foreman (creator of iminlikewithyou.com).

At some point last semester I showed iminlikewithyou hamster battle to my undergrad students towards the end of class.... probably one of the funniest moments of that class.

Posted in BitmapData, misc | Tagged , | 1 Comment

Jumping on a 2D Circle

Actionscript:
  1. var circle:Shape = Shape(addChild(new Shape()));
  2. with(circle.graphics) beginFill(0xCCCCCC), drawCircle(0,0,100);
  3. circle.x = stage.stageWidth / 2;
  4. circle.y = stage.stageHeight / 2;
  5.  
  6. var charWorld:MovieClip = MovieClip(addChild(new MovieClip()));
  7. charWorld.x = circle.x ;
  8. charWorld.y = circle.y - 100 - 10
  9. charWorld.thetaSpeed = 0;
  10. charWorld.theta  = -Math.PI / 2;
  11.  
  12. var char:MovieClip = MovieClip(charWorld.addChild(new MovieClip))
  13. with(char.graphics) beginFill(0x000000), drawRect(-10,-10,10,10);
  14. char.posY = 0;
  15. char.velY = 0;
  16.  
  17. addEventListener(Event.ENTER_FRAME, onRunChar);
  18. function onRunChar(evt:Event):void {
  19.    
  20.     char.velY += 1;
  21.     char.posY += char.velY;
  22.    
  23.     charWorld.thetaSpeed *= .6;
  24.     charWorld.theta += charWorld.thetaSpeed;
  25.    
  26.     if (key[Keyboard.UP]){
  27.         if (char.y == 0){
  28.           char.velY = -10;
  29.         }
  30.     }
  31.    
  32.     if (key[Keyboard.RIGHT]){
  33.         charWorld.thetaSpeed = .1;
  34.     }
  35.    
  36.     if (key[Keyboard.LEFT]){
  37.         charWorld.thetaSpeed = -.1;
  38.     }
  39.    
  40.     if (char.posY> 0){
  41.         char.posY = 0;
  42.     }
  43.    
  44.     char.y = char.posY;
  45.    
  46.     charWorld.x = circle.x + 100 * Math.cos(charWorld.theta);
  47.     charWorld.y = circle.y + 100 * Math.sin(charWorld.theta);
  48.     charWorld.rotation = Math.atan2(circle.y- charWorld.y, circle.x - charWorld.x) / Math.PI * 180 - 90;
  49. }
  50.  
  51. var key:Object = new Object();
  52. stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyPressed);
  53. stage.addEventListener(KeyboardEvent.KEY_UP, onKeyReleased);
  54. function onKeyPressed(evt:KeyboardEvent):void {
  55.     key[evt.keyCode] = true;
  56.     key.keyCode = evt.keyCode;
  57. }
  58.  
  59. function onKeyReleased(evt:KeyboardEvent):void {
  60.     key[evt.keyCode] = false
  61. }

For some reason I felt like posting the swf.... have a look here.

This is one that's been kicking around in my head for awhile - finally got around to writing it. It creates a circle and a small black box. The box walks and jumps on the circle with key input (left arrow, right arrow and up arrow).

There's an odd trick going on here. Basically, the box (or char) movieClip is nested inside another clip. Within this clip (charWorld) the box moves on the y axis (jumping/up key). The charWorld clip orbits around the circle and rotates toward the center of the circle - sine/cosine are used for the orbit so the left and the right keys control the speed of theta.

Posted in misc, motion | Tagged , | Leave a comment