Browse the notebook

Category: motion

drawPath() Boxes

const TWO_PI:Number = Math.PI * 2;
var boxNum:int = 3000;
var pointNum:int = boxNum * 10;
var rot:Vector.<Number> = new Vector.<Number>();
var posX:Vector.<Number> = new Vector.<Number>();
var posY:Vector.<Number> = new Vector.<Number>();
var velX:Vector.<Number> = new Vector.<Number>();
var velY:Vector.<Number> = new Vector.<Number>();
var scale:Vector.<Number> = new Vector.<Number>();
var rotSpeed:Vector.<Number> = new Vector.<Number>();
var geometry:Vector.<Number> = new Vector.<Number>();
var cmds:Vector.<int> = new Vector.<int>();

var stageWidth:Number = stage.stageWidth;
var stageHeight:Number = stage.stageHeight;

populateGeom();
function populateGeom():void{
	for (var i:int = 0; i<pointNum; i+=10){
		posX.push(Math.random() * stageWidth);
		posY.push(Math.random() * stageHeight);
		velX.push(Math.random()*4 - 2);
		velY.push(Math.random()*4 - 2);
		scale.push(Math.random() * 1 + .2);
		rot.push(Math.random() * TWO_PI);
		rotSpeed.push(Math.random() * 0.2 - 0.1);
		geometry[i] = 0;
		geometry[i + 1] = 0;
		cmds.push(1);
		geometry[i + 2] =  0;
		geometry[i + 3] =  0;
		cmds.push(2);
		geometry[i + 4] =  0;
		geometry[i + 5] =  0;
		cmds.push(2);
		geometry[i + 6] = 0;
		geometry[i + 7] =  0;
		cmds.push(2);
		geometry[i + 8] =  0;
		geometry[i + 9] =  0;
		cmds.push(2);
	}
}

function run():void{
	var inc:int = 0;
	var xt:Number, yt:Number, s:Number, r:Number, rs:Number;
	var cos:Number, sin:Number;
	var cosN:Number, cosP:Number, sinN:Number, sinP:Number;
	for (var i:int = 0; i<pointNum; i+=10){
		xt = posX[inc] += velX[inc];
		yt = posY[inc] += velY[inc];
		
		if (xt < 0 || xt > stageWidth){
			velX[inc] *= -1;
		}
		if (yt < 0 || yt > stageHeight){
			velY[inc] *= -1;
		}
		 
		s = scale[inc];
		r = rot[inc] += rotSpeed[inc];
		inc++;
		 
		cos = Math.cos(r);
		sin = Math.sin(r);
		
        cosN = -10  * cos;
		cosP = 10  * cos
		sinN = -10 * sin;
		sinP = 10 * sin;
		geometry[i] = xt + (cosN - sinN) * s;
		geometry[i + 1] = yt + (cosN + sinN) * s;
		geometry[i + 2] = xt + (cosP - sinN) * s;
		geometry[i + 3] = yt + (cosN + sinP) * s;
		geometry[i + 4] = xt + (cosP - sinP) * s;
		geometry[i + 5] = yt + (cosP + sinP) * s;
		geometry[i + 6] = xt + (cosN - sinP) * s;
		geometry[i + 7] = yt + (cosP + sinN) * s;
		geometry[i + 8]  = geometry[i];
		geometry[i + 9]  = geometry[i + 1];
	}
}

addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
     run();
	 graphics.clear();
	 graphics.beginFill(0x000000);
	 graphics.drawPath(cmds, geometry,GraphicsPathWinding.NON_ZERO);
	 graphics.endFill();
}

I was messing with drawPath() today and did this snippet as a sort of performance test… the code isn’t fully optimized but it’s clear that drawPath() is rather fast. I wish that there were beginFill() and lineStyle() drawing commands that worked with it though… oh yeah, that’s what IGraphicsData is for…

Read snippet: drawPath() Boxes

QuickBox2D Chain

import com.actionsnippet.qbox.*;

var sim:QuickBox2D = new QuickBox2D(this, {debug:true});
// use the box2D default renderer (debug : true)

sim.createStageWalls();

// anchor
var pre:QuickObject = sim.addCircle({x:9, y:3, radius:.5, density:0});

// create a chain of boxes
for (var i:int = 0; i<12; i++){
  var curr:QuickObject = sim.addBox({x:10 + i, y:3, width:.9, height:.9, angularDamping:1});
									                             
  // currently always adds a b2DistanceJoint
  sim.addJoint({a:pre.body, b:curr.body, 
			               x1:9 + i, y1: 3, x2: 10 + i, y2:3, collideConnected:false});
  pre = curr;
}

// add a circle, use CCD (isBullet)
sim.addCircle({x:20, y:10, radius:1, isBullet:true});

// start simulation
sim.start();
sim.mouseDrag();

This snippet uses my QuickBox2D library to create a chain. QuickBox2D is a wrapper for Box2DFlashAS3 that greatly simplifies world setup, instantiation and graphical skinning of rigid bodies (more info on yesterdays post)…

See the swf here .

Download Box2DFlashAS3 here

Download QuickBox2D Alpha 104 here

See yesterdays post for additional information. I’m still in the process of writing some docs and possibly a tutorial for QuickBox2D… hopefully I’ll finish with that soon…

If your somewhat familiar with Box2D and just want to just dig right in, the following scrap of information may help:


// Methods of the QuickBox2D class, used for creating rigid bodies and joints
addCircle();
addBox();
addPoly();
// currently only creates b2DistanceJoints
addJoint();

these function all take one parameter Object as their argument. The common parameter properties for rigid bodies are:

// QuickBox2D simpleRender settings:
lineColor:0x000000
lineAlpha:1
lineThickness:0
fillColor:0xCCCCCC
fillAlpha:1
// wrapped Box2D properties
x:3
y:3
density:1
friction:0.5
restitution:0.2
angle: 0
linearDamping:0
angularDamping:0
isBullet:false,
fixedRotation:false
allowSleep: true
isSleeping:false
// advanced use
maskBits:0xFFFF
categoryBits:1
groupIndex:0

//and the specific param properties are:
addCircle() : radius
addBox() : width, height
addPoly() : verts

//Joints have the following properties... not going to bother explaining these until I write the docs:
addJoint() : a, b, x1, x2, y1, y2

Read snippet: QuickBox2D Chain

Quick Box2D

[SWF(backgroundColor = 0x333333)]

import com.actionsnippet.qbox.*;

// setting debug = true, will use the Box2D debug rendering
var sim:QuickBox2D=new QuickBox2D(this, {debug:false, gravityY:10});

// creates static boxes on all sides of the screen
sim.createStageWalls({lineAlpha:0, fillColor:0xFF9900});

// add 25 boxes
for (var i:int = 0; i<25; i++){
	var xp:Number = 3 + (i % 5);
	var yp:Number = 1 + int( i / 5);
	sim.addBox({x:xp, y:yp, width:1, height:1, fillColor: i * 10 << 16, lineAlpha:0, angularDamping:5});
}

sim.addBox({x:7, y:10, width:3, height:.2, angle:-.3, density:0,lineAlpha:0, fillColor:0xFF9900});

sim.addCircle({x:3, y:14, radius:2, fillColor:0xCC0000, lineColor:0x333333});

sim.addPoly({x:13, y:5, verts:[[1,0,2,2,1,1.33],[1,0,1,1.33,0,2]], angle: .4, density:1});

// begins the simulation
sim.start();

// all non-static objects can be dragged
sim.mouseDrag();

I’ve been messing with Box2D for the past few weeks – I decided to create a library (QuickBox2D) for quick prototyping. This snippet makes use of this library, so in order to run it you’ll need to download the zip included at the end of this post…

If you don’t already know, Box2D is a great physics library created by Erin Catto. It was ported to AS3 by Matthew Bush and John Nesky. You can download the AS3 version of the library here.

Today’s snippet creates this swf (click any image to view demo):

QuickBox2D

QuickBox2D simplifies Box2D instantiation and makes it easy to skin rigid bodies with Library assets… normally you need to do something like this to create a box in Box2D:

var bodyDef:b2BodyDef = new b2BodyDef();
bodyDef.position.Set(3, 3);
var boxDef:b2PolygonDef = new b2PolygonDef();
boxDef.SetAsBox(1, 1);
boxDef.density = 1;
var body:b2Body = w.CreateBody(bodyDef);
body.CreateShape(boxDef);
body.SetMassFromShapes();

and this doesn’t include setting up the physics world and the main loop to run the simulation… after writing a good deal of repetitive code I decided to make QuickBox2D. After writing lots of QuickBox2D style instantiation I decided to create an editor so that I could just draw circles, boxes, polygons and joints and then test and save them in real time (will be posting this editor in the near future). The next few posts will be about this mini-library and I’ll be writing up some real documentation for it… as of now, it doesn’t have any…

You can download Box2D here

You can download QuickBox2D here

and you can view some AS3 Box2D demos here

Read snippet: Quick Box2D

Random Equation Attractor

[SWF(width = 800, height = 800)]
var a:Number = 0.19;
var b:Number = .9;
var c:Number = 1.3
var xn1:Number = 5;
var yn1:Number = 0;
var xn:Number, yn:Number;

var scale:Number =40;
var iterations:Number = 20000;

function f(x:Number):Number{
	// too lazy to simplify this at the moment
	return((x + .1 + x * (a - c) * x) / (1.1 + a * (c*c + a*a)  * x * x )) * 1.3;
}
 
var canvas:BitmapData = Bitmap(addChild(new Bitmap(new BitmapData(800,800,false,0xEFEFEF)))).bitmapData;
																	   
addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
	
    canvas.fillRect(canvas.rect, 0xEFEFEF);
	 
	c +=  ((stage.stageWidth/2 - mouseX) / 8000 - c) / 2;
	
	xn1 = 0;
	yn1 = 0;
	for (var i:int = 0; i<iterations; i++){
		  xn = xn1;
		  yn = yn1;
		  
          xn1 = -xn - a + c +  f(yn);
          yn1 = -xn + c * f(xn * yn); 
		  canvas.setPixel( 380 + xn1 * scale, 450 + yn1 * scale, 0x000000);
	}
}

I was randomly messing around with strange attractors a few weeks back and this is one of the better results…. I arbitrarily altered equations until I got a nice result. You can move your mouse left and right to alter some of the params. Here is what it looks like when your mouse is in the middle of the screen:

Read snippet: Random Equation Attractor

Infinity Scroller (repeating navigation)

var buttonsWidth:Number = 0;
var buttonsA:Sprite = new Sprite();
var buttonsB:Sprite = new Sprite();
var nav:Sprite = new Sprite();

var buttonData:Array = ["one", "two", "three", "four", "five", "six", 
                                    "seven", "eight", "nine", "ten"];

buildNav();

function buildNav():void{
	nav.addChild(buttonsA);
        nav.addChild(buttonsB);
        addChild(nav);
	buildButtons(buttonsA);
	buttonsWidth = buttonsA.width;
	buildButtons(buttonsB);
	buttonsB.x = buttonsWidth;
}

function buildButtons(container:Sprite):void{
	for (var i:int = 0; i rightSide){
			velX = -navSpeed;
		}
		if (nav.x < -buttonsWidth){
			nav.x = -navSpeed;
		}
		if (nav.x > -navSpeed){
			nav.x = -buttonsWidth 
		}
	}
	velX *=.9;
	nav.x += velX;
}

This snippet creates a navigation that will scroll to the left or right forever – the buttons will simply repeat. I’m not a big fan of this kind of navigation – especially if you have lots of buttons, but it seems to be a common request. This technique can be modified for use in a side-scroller style game.

Just added a new syntax highlighter, please posts comments if you have any issues viewing this snippet.

Read snippet: Infinity Scroller (repeating navigation)

Pendulums

var mouseSpeedX:Number = 0;
var prevX:Number = 0;

var pends:Array = new Array();
for (var i:int = 0; i<10; i++){
       pends.push(makePendulum(100+ i * 40, 100, 15, 100 + i * 10));
}

addEventListener(Event.ENTER_FRAME, onRun);
function onRun(evt:Event):void {
       // mouseSpeed
       mouseSpeedX = prevX - mouseX;
       prevX = mouseX;

       for (var i:int = 0; i<pends.length; i++) pends[i]();
}

function makePendulum(xp:Number, yp:Number, rad:Number, leng:Number):Function {
       var rot:Number = 0;
       var rotDest :Number = 0;
       var rotVel:Number = 0
       var string:Shape = Shape(addChild(new Shape()));
       var ball:Sprite = Sprite(addChild(new Sprite()));
       ball.buttonMode = true;
       with(ball.graphics) beginFill(0xFF0000), drawCircle(0,leng, rad);
       ball.x = xp;
       ball.y = yp;
       string.x = ball.x;
       string.y = ball.y;
       ball.addEventListener(MouseEvent.ROLL_OVER,function(){
             rotDest = mouseSpeedX;
       });
       return function(){
           // force rotDest back to 0
           rotDest *= .8;
               // elasticity (hooke's)
               rotVel += (-1.9 * (rot - rotDest) - rotVel) / 4;
               rot += rotVel
               ball.rotation = rot;
               // draw string:
               string.graphics.clear();
               string.graphics.lineStyle(0,0);
               var pnt:Point = ball.localToGlobal(new Point(0, leng))
               string.graphics.curveTo(0, leng / 2, pnt.x - ball.x, pnt.y-ball.y);
  }
}

This is a variation on something I wrote in response to a student question. It creates a few pendulums that can be pushed with the mouse.

Read snippet: Pendulums

Slider Navigation

var thumbNum:Number = 20;
var spacing:Number = 10;
var thumbs:MovieClip = new MovieClip();
addChild(thumbs);
for (var i:int = 0; i<thumbNum; i++){
	var t:MovieClip = new MovieClip();
	with(t.graphics) beginFill(0x666666), drawRect(0,0,100,50);
	t.x = i * (t.width + spacing);
	t.y = 5;
    t.buttonMode = true;
	thumbs.addChild(t);
}
var minX:Number = stage.stageWidth - thumbs.width - spacing;
var destX:Number = thumbs.x = spacing;
var velX:Number = 10;
var stageThird:Number =  stage.stageWidth / 3;
var right:Number = stageThird * 2;
var left:Number  = stageThird;
addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
	if (mouseX > right){
	   destX -= velX;
	}
	if (mouseX < left){
		destX += velX;
	}
	if (destX < minX){
		destX = minX;
	}
	if (destX > spacing){
		destX = spacing;
	}
	thumbs.x += (destX - thumbs.x) /4;
}

This snippet shows a technique for a common type of navigation.

Read snippet: Slider Navigation

Hippopede

x=stage.stageWidth/2;
y=stage.stageHeight/2;
var xp:Number = 0, yp:Number = 0;
var r:Number = 0, t:Number = 0;
var speed:Number=.1;

var range:Number=.2;
var b:Number = 20;
var a:Number = b * range;
var orbit:Number = 2 * Math.PI + speed;
 
graphics.lineStyle(0,0x000000);

addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
	var sin:Number=Math.sin(t);
	r =  Math.sqrt(4 * b * (a - b * (sin * sin)));
	xp= r * Math.cos(t);
	yp= r * sin;
	
    if (t==0) {
		graphics.moveTo(xp, yp);
	} else {
		graphics.lineTo(xp, yp);
	}

	t += speed;
	
	if (t > orbit) {
		range += .5;
		a= b * range;
		t= 0;
	}
}

The above draws a Hippopede

Read snippet: Hippopede

Fermat’s Spiral

x = stage.stageWidth / 2;
y = stage.stageHeight / 2;

var xp:Number = 0, yp:Number = 0;
var r:Number = 0, t:Number = 0;
var speed:Number = .07;
var scale:Number = 20;

var plot0:Shape = Shape(addChild(new Shape()));
var plot1:Shape = Shape(addChild(new Shape()));
plot0.graphics.lineStyle(0,0x000000);
plot1.graphics.lineStyle(0,0x000000);

addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
	 r =  scale * Math.sqrt(t);
	 xp =  r * Math.cos(t);
	 yp =  r * Math.sin(t);
	 t += speed;
	 
	plot0.graphics.lineTo(xp, yp);
    plot1.graphics.lineTo(-xp, -yp);
}

This snippet draws Fermat’s Spiral.

Read snippet: Fermat’s Spiral