Browse the notebook

Tag: Box2D

QuickBox2D 3D

import com.actionsnippet.qbox.*;
[SWF(width = 800, height=600, backgroundColor=0x000000, frameRate = 60)]

var main:MovieClip = MovieClip(addChild(new MovieClip()));
main.z = 500;
main.rotationX = -40;
 
var sim:QuickBox2D = new QuickBox2D(main);

sim.createStageWalls({fillColor:0x1133CC});
sim.setDefault({lineColor:0xFFFFFF, fillColor:0x113355});

for (var i:int = 0; i<30; i++){
	var b:QuickObject = sim.addBox({x:Math.random()*10 + 3, y:Math.random()*10 + 3, 
									width:0.25 + Math.random()*2, height:0.25 + Math.random()*2});
}

sim.start();
sim.mouseDrag();

This demo shows how you can render your QuickBox2D simulation to somewhere other than the main timeline. In this case, I render to a MovieClip that has altered z and and rotationX properties.


Have a look at the swf…

Read snippet: QuickBox2D 3D

QuickBox2D Contacts Part 3

import com.actionsnippet.qbox.*;
import Box2D.Common.Math.*;

[SWF(width = 800, height = 600, backgroundColor = 0x222222, frameRate=60)]

var sim:QuickBox2D = new QuickBox2D(this, {debug:false});

sim.createStageWalls();

var cup:QuickObject = sim.addPoly({x:3, y:13, wireframe:false, density:0, 
								   points:[0,0, .5, 0, .5,3,  2,3,  2,0,  2.5,0, 2.5, 3.5, 0, 3.5, 0,0]});
var deleter:QuickObject = sim.addBox({x:4.251, y: 15.9, width:1.5, height:0.25, fillColor:0xFF0000});

// use a distance joint instead of a group since isCurrentContact 
// uses the QuickObject.body property
sim.addJoint({a:cup.body, b:deleter.body, x1:deleter.x, y1:deleter.y+0.5});

var circles:Array = [];
for (var i:int = 0; i<10; i++){
	circles[i] = sim.addCircle({x:8 + i, y:5, radius:0.5});
}
var boxes:Array = [];
for (i = 0; i<10; i++){
	boxes[i] = sim.addBox({x:8.5 + i, y:10, width:0.5, height:0.5});
}

sim.start();
sim.mouseDrag();

var shootVec:b2Vec2 = new b2Vec2(4, -30);

// when circles are dropped in the cup they are destroyed
// when boxes are droppped in the cup their linear velocity is altered
var contacts:QuickContacts = sim.addContactListener();
contacts.addEventListener(QuickContacts.ADD, onAdd);
function onAdd(evt:Event):void{
	for (var i:int = 0; i<circles.length; i++){
		var circ:QuickObject = circles[i];
		if (contacts.isCurrentContact(circ, deleter)){
			// it's ok to destroy QuickObject here because they are only actually
			// destroyed after the timeStep has finished at the end of QuickBox2D's 
			// internal loop
			circ.destroy();
		}
	 } 
	 for (i = 0; i<boxes.length; i++){
		var box:QuickObject = boxes[i];
		if (contacts.isCurrentContact(box, deleter)){
			box.body.SetLinearVelocity(shootVec);
		}
	 }
}

Note: This snippet requires QuickBox2D 1.0 or greater

Here is another example of using QuickBox2D style contacts. In this example there is a cup with a small red box inside of it. When circles are placed inside the cup they are destroyed. When boxes are placed inside the cup they are shot upward.


Have a look at the swf…

A Note About Creation and Destruction

It’s worth noting that in Box2D your not supposed to destroy or create rigid bodies within the listener function for any contact events. QuickBox2D gets around this restriction by flagging the rigid body for destruction and destroying it at the end of QuickBox2D’s internal loop. This is not the case with creation of rigid bodies – if you attempt to create a rigid body within one of these listener functions you will get an error. See QuickBox2D Contacts Part 1 for an example of creating rigid bodies when there is a collision.

Read snippet: QuickBox2D Contacts Part 3

QuickBox2D Contacts Part 2

import com.actionsnippet.qbox.*;

[SWF(width = 800, height = 600, backgroundColor = 0x000000, frameRate=60)]

var sim:QuickBox2D = new QuickBox2D(this);

sim.createStageWalls();

var boxA:QuickObject = sim.addBox({x:3, y:3, width:2, height:2});
var boxB:QuickObject = sim.addBox({x:3, y:6, width:2, height:2});

// add a few extra circles to show that there are other contacts
// occuring
for (var i:int = 0; i<10; i++){
	sim.addCircle({x:5 + i, y:4, radius:0.1 + Math.random()});
}

sim.start();
sim.mouseDrag();

// when boxA touches boxB change their alpha values to 50%
var contacts:QuickContacts = sim.addContactListener();
contacts.addEventListener(QuickContacts.ADD, onAdd);
contacts.addEventListener(QuickContacts.PERSIST, onPersist);
contacts.addEventListener(QuickContacts.REMOVE, onRemove);
function onAdd(evt:Event):void{
	if(contacts.isCurrentContact(boxA, boxB)){
		boxA.userData.alpha = 0.5;
		boxB.userData.alpha = 0.5;
	}
}
function onPersist(evt:Event):void{
	if(contacts.isCurrentContact(boxA, boxB)){
		boxA.userData.alpha = 0.5;
		boxB.userData.alpha = 0.5;
	}
}
function onRemove(evt:Event):void{
	if(contacts.isCurrentContact(boxA, boxB)){
		boxA.userData.alpha = 1;
		boxB.userData.alpha = 1;
	}
}

Note: This snippet requires QuickBox2D 1.0 or greater

This is another example showing how to make use of QuickBox2D contacts. In this example three contact events are used ADD, PERSIST and REMOVE. When the two boxes are touching their alpha values are set to 50% – otherwise their alpha values are at 100%.


Have a look at the swf…

A Little Detail About the Events

The ADD event is dispatched when a new b2ContactPoint is generated. The PERSIST event is dispatched as long as the b2ContactPoint exists for more than one timeStep – and from what I can tell, is dispatched until the rigid bodies it is associated with go to sleep. The REMOVE event is called when the b2ContactPoint is removed, meaning the rigid bodies that caused the creating of the b2ContactPoint are no longer touching.

You need to be careful with the code that you place inside the listener functions for these events. The reason being that these events are called many many times for every contact point in the simulation.

Read snippet: QuickBox2D Contacts Part 2

QuickBox2D Contacts Part 1

import com.actionsnippet.qbox.*;
import Box2D.Common.Math.*;

[SWF(width = 800, height = 600, backgroundColor = 0x000000, frameRate=60)]

var sim:QuickBox2D = new QuickBox2D(this, {debug:false, frim:true});

sim.createStageWalls();

var boxA:QuickObject = sim.addBox({x:3, y:3, width:2, height:2, fillColor:0xFF0000});
var boxB:QuickObject = sim.addBox({x:3, y:6, width:2, height:2, fillColor:0xFF0000});

sim.start();
sim.mouseDrag();

// when boxA touches boxB a circle QuickObject is created
var contacts:QuickContacts = sim.addContactListener();
// listen for contact points being added
contacts.addEventListener(QuickContacts.ADD, onAdd);
function onAdd(evt:Event):void{
	// see if this contact event is associated with boxA and boxB
	if(contacts.isCurrentContact(boxA, boxB)){
		// get the location of the collision in world space
		var loc:b2Vec2 = contacts.currentPoint.position;
		// you cannot create new QuickObjects inside this listener function
		// so we just give a 5 ms delay
		setTimeout(sim.addCircle, 5, {x:loc.x, y:loc.y});
	}
}

Note: This snippet requires QuickBox2D 1.0 or greater

I’ve created a few simple examples to show how to use QuickBox2D contact listeners. This is the first one. When two boxes collide circles are added to the simulation at the point of collision.


Have a look at the swf…

Read snippet: QuickBox2D Contacts Part 1

QuickBox2D 1.0

QuickBox2D 1.0 is ready. It contains a bunch of small bug fixes and a few new features that I’ll be demoing in the next few posts. The main new features relate to collision detection and simple event sequencing.

Go download it from here…

I also spent some time updating the docs…. everything is documented… I may add additional documentation text over the next few days…

A detailed tutorial for QuickBox2D is also in the works…

[EDIT]

Also, if you find any bugs send me an e-mail (see about page for e-mail)… if there are any bugs left I’d like to fix the asap…. You can also feel free to e-mail any API suggestions…

Read snippet: QuickBox2D 1.0

QuickBox2D addTimeStepSequence() Preview

import com.actionsnippet.qbox.*;

[SWF(width = 800, height = 600, backgroundColor = 0xFFFFFF, frameRate=60)]

// hide everything for 300 ms
visible = false;
setTimeout(function():void{ visible = true}, 300);

var canvas:MovieClip = MovieClip(addChild(new MovieClip()));

var sim:QuickBox2D = new QuickBox2D(canvas, {debug:true});

sim.setDefault({fillColor:0x666666, lineAlpha:0})
sim.createStageWalls();

var boxA:QuickObject = makeLever(6 , 17.5, 9, "right", 0.7);
var circleA:QuickObject = sim.addCircle({x:2, y:3, radius:0.5, mass:4});
sim.addBox({x:15, y:12, width:2, height:0.5, density:0})
var circleB:QuickObject = sim.addCircle({x:16, y:11, radius:0.5, mass:4, friction:0.2, fillColor:0});
sim.addBox({x:20.5, y:12, width:2, height:0.3, angle:-0.6, restitution:4.6, density:0})
var boxB:QuickObject = makeLever(20 , 17.5, 8, "right", 1);
sim.addBox({x:9, y:9, width:2, height:0.5, density:0});
var circleC:QuickObject = sim.addCircle({x:9, y:9, radius:0.5, mass:4, friction:0.2, fillColor:0});
sim.addBox({x:7, y:12, width:2, height:0.5, angle:-0.3, density:0, restitution:2});
sim.addBox({x:3, y:7, width:2, height:0.5, angle:Math.PI/2 - 0.22, density:0, restitution:3});
sim.addBox({x:13, y:15, width:2, height:0.5, density:0, angle:0.3, restitution:1});
sim.addBox({x:25.5, y:7, width:2, height:0.3, angle:-0.2, restitution:0.5, density:0})
sim.addBox({x:26, y:10, width:1, height:1, density:0,restitution:0.3});
sim.addBox({x:25, y:19, width:2, height:0.5, density:0, restitution:3.5, angle:-0.99});
sim.addBox({x:1, y:17, width:0.3, height:2, density:0, angle:-0.15, restitution:0.999});
sim.addBox({x:0.7, y:11, width:0.3, height:2, density:0, angle:-0.19, restitution:20})
sim.addBox({x:0.4, y:11.5, width:1, height:0.3, density:0})

function makeLever(xp:Number, yp:Number, boxWidth:Number, dir:String, off:Number=0, hasBox:Boolean=true):QuickObject{
	var angle:Number, xpos:Number, weight:QuickObject
	if (dir == "right"){
		angle = 0.43;
		xpos = xp + boxWidth / 2 - off;
	}else{
		angle = -0.43;
		xpos = xp - boxWidth / 2 + off;
	}
	if (hasBox){
	  weight = sim.addBox({x:xpos , y:yp, width:0.5, height:0.5, angle:angle, fillColor:0});
	}
	var plank:QuickObject = sim.addBox({x:xp, y:yp, width:boxWidth, height:0.5, angle:angle});
	var fulcrum:QuickObject = sim.addPoly({x:xp, y:yp, verts:[[0,0, 1,2, -1,2]], density:0})
	sim.addJoint({type:"revolute", a:plank.body, b:fulcrum.body, collideConnected:false});
	return weight;
}

sim.start();

// new feature, calls a function after a specific number of box2D timeSteps
sim.addTimeStepSequence({time:70, callback:cam, args:[boxA]}, 
						{time:150, callback:cam, args:[circleB]},
						{time:200, callback:cam, args:[boxB, 1.3]},
						{time:580, callback:cam, args:[circleC]},
						{time:700, callback:cam, args:[circleC, 1.1]},
						{time:810, callback:cam, args:[boxB, 1.4]},
						{time:870, callback:cam, args:[boxB, 3]},
						{time:890, callback:cam, args:[boxB, 1.1]},
						{time:1200,callback:cam, args:[boxB, 1]});

// set values for matrix pan and zoom
function cam(quickObj:QuickObject, scale:Number=-1):void{
	currQuick = quickObj;
	if (scale != -1){
		scaleDest = scale;
	}
}								  
				
// matrix pan and zoom
var scale:Number = 1;
var scaleDest:Number = 1.2;
var down:Boolean = false;
var dx:Number = 0, dy:Number = 0, time:Number = 0;
var currQuick:QuickObject = circleA;
addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
   scale += (scaleDest - scale) / 8; 
   dx += (currQuick.x*30 - dx) / 8;
   dy += (currQuick.y*30 - dy) / 8;
   // matrix zoom/pan
    var m:Matrix = canvas.transform.matrix;
   m.identity();
   m.translate(-dx,-dy);
   m.scale(scale, scale);
   m.translate(dx,dy);
   canvas.transform.matrix = m;
}

This is a preview of an upcoming feature for the QuickBox2D 1.0 release. It shows how addTimeStepSequence() is going to work. QuickBox2D 1.0 will have an internal timer counting the number of world steps that Box2D has taken. This is really useful for sequencing in conjunction with QuickBox2D’s default FRIM (framerate independent motion).

This snippet uses addTimeStepSequence() to move a 2D camera around the simulation (from yesterdays post) – the camera pans and zooms… If you’d like to use addTimeStepSequence() simply replace your QuickBox2D.as file with this updated one:

I plan to release QuickBox2D 1.0 on monday – so I didn’t think it made sense to do alpha 110 just for this one change…


Have a look at the swf….

Read snippet: QuickBox2D addTimeStepSequence() Preview

QuickBox2D Vaguely Goldberg-esque

import com.actionsnippet.qbox.*;
import Box2D.Common.Math.*;
import Box2D.Collision.Shapes.*;

[SWF(width = 800, height = 600, backgroundColor = 0xFFFFFF, frameRate=60)]

// hide everything for 300 ms
visible = false;
setTimeout(function():void{ visible = true}, 300);

var sim:QuickBox2D = new QuickBox2D(this, {debug:false});

sim.setDefault({fillColor:0x666666, lineAlpha:0})
sim.createStageWalls();

makeLever(6 , 17.5, 9, "right", 0.7);

var circleA:QuickObject = sim.addCircle({x:2, y:3, radius:0.5, mass:4});

sim.addBox({x:15, y:12, width:2, height:0.5, density:0})

sim.addCircle({x:16, y:11, radius:0.5, mass:4, friction:0.2, fillColor:0});

sim.addBox({x:20.5, y:12, width:2, height:0.3, angle:-0.6, restitution:4.6, density:0})
 
makeLever(20 , 17.5, 8, "right", 1);
 
sim.addBox({x:9, y:9, width:2, height:0.5, density:0});

sim.addCircle({x:9, y:9, radius:0.5, mass:4, friction:0.2, fillColor:0});

sim.addBox({x:7, y:12, width:2, height:0.5, angle:-0.3, density:0, restitution:2});

sim.addBox({x:3, y:7, width:2, height:0.5, angle:Math.PI/2 - 0.22, density:0, restitution:3});

sim.addBox({x:13, y:15, width:2, height:0.5, density:0, angle:0.3, restitution:1});

sim.addBox({x:25.5, y:7, width:2, height:0.3, angle:-0.2, restitution:0.5, density:0})

sim.addBox({x:26, y:10, width:1, height:1, density:0,restitution:0.3});

sim.addBox({x:25, y:19, width:2, height:0.5, density:0, restitution:3.5, angle:-0.99});

sim.addBox({x:1, y:17, width:0.3, height:2, density:0, angle:-0.15, restitution:0.999});

sim.addBox({x:0.7, y:11, width:0.3, height:2, density:0, angle:-0.19, restitution:20})

sim.addBox({x:0.4, y:11.5, width:1, height:0.3, density:0})

function makeLever(xp:Number, yp:Number, boxWidth:Number, dir:String, off:Number=0, hasBox:Boolean=true):QuickObject{
	var angle:Number, xpos:Number, weight:QuickObject
	if (dir == "right"){
		angle = 0.43;
		xpos = xp + boxWidth / 2 - off;
	}else{
		angle = -0.43;
		xpos = xp - boxWidth / 2 + off;
	}
	
	if (hasBox){
	  weight = sim.addBox({x:xpos , y:yp, width:0.5, height:0.5, angle:angle, fillColor:0});
	}

	var plank:QuickObject = sim.addBox({x:xp, y:yp, width:boxWidth, height:0.5, angle:angle});
	var fulcrum:QuickObject = sim.addPoly({x:xp, y:yp, verts:[[0,0, 1,2, -1,2]], density:0})
	sim.addJoint({type:"revolute", a:plank.body, b:fulcrum.body, collideConnected:false});
	return weight;
}

sim.start();

Just playing around with QuickBox2D and brainstorming about a few features for the first non-alpha release. If you have any feature suggestions, please feel free to leave them in the comments of this post (see the end of this post for a list features to come in QuickBox2D 1.0).


Have a look at the swf…

Upcoming Features for QuickBox2D
Simplified collision detection – will hopefully be something like hitTest for QuickObject instances.

QuickBox2D.totalTimeSteps – variable that keeps track of how many times b2World.Step() was called – useful for simple sequencing.

QuickBox2D.addTimeStepEvent(callAt:Number, callback:Function) – calls a function when QuickBox2D.totalTimeSteps is equal to the callAt value.

QuickBox2D.stepEvent – an event dispatched every time b2World.Step() is called.

New Renderer [probably not for 1.0 release]
I’ve been toying with the idea of creating a new renderer for QuickBox2D. Currently all rigid bodies are DisplayObjects… this is great if you plan on skinning all your rigid bodies, but if you just have flat colors it is slower than debug draw – also, debug draw mode could be optimized a good deal. So I may create a simpler, faster renderer that draws to one Graphics instance (like debug draw mode) – but abides by QuickBox2D’s simple rendering params like lineColor, fillColor, lineAlpha etc…

I also need to decide how to skin the rest of the joints, currently skins only work for distance joints…

Read snippet: QuickBox2D Vaguely Goldberg-esque

QuickBox2D groupIndex Platforms

import com.actionsnippet.qbox.*;
import Box2D.Common.Math.*;
import Box2D.Collision.Shapes.*;

[SWF(width = 800, height = 600, backgroundColor = 0x000000)]

var sim:QuickBox2D = new QuickBox2D(this);

sim.createStageWalls();

var boxNum:int = 8;
var boxes:Array = [];
var xp:Number, yp:Number, w:Number;
for (var i:int = 0; i<boxNum; i++){
	xp = 3 + Math.random() * 10;
	yp = 4 + i * 2;
	w = 2 + Math.random() * 3;
	boxes.push(sim.addBox({x: xp, y: yp, width: w, height: 0.5, density:0, groupIndex:-1, fillColor:0xFF6532}));
}

var char:QuickObject = sim.addBox({x:2, y:18, width:1, height:2, allowSleep:false, groupIndex:-1, fillColor:0x0099CC});

sim.start();

var charVel:b2Vec2;
var charVelAng:Number;

addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
	 
	charVel = char.body.GetLinearVelocity();
	charVelAng =  char.body.GetAngularVelocity();
	var filter:b2FilterData;
	
	if (key[Keyboard.RIGHT]){
		charVel.x += 1
		char.body.SetLinearVelocity(charVel);
		charVelAng += 1;
		char.body.SetAngularVelocity(charVelAng);
	}
	if (key[Keyboard.LEFT]){
		charVel.x -=1;
		char.body.SetLinearVelocity(charVel);
		charVelAng -= 1;
		char.body.SetAngularVelocity(charVelAng);
	}
	if (key[Keyboard.UP] && sim.w.GetContactCount() > 0){
		 charVel.y = -10;
		 trace(charVel.y);
		 char.body.SetLinearVelocity(charVel);
		 charVelAng *= 0.8;
		 char.body.SetAngularVelocity(charVelAng);
	}
	for (var i:int = 0; i<boxes.length; i++){
		var rect:Rectangle = char.userData.getRect(this);
		if (rect.bottom / 30 < boxes[i].y){
			filter = boxes[i].shape.GetFilterData();
			filter.groupIndex = 1;
			boxes[i].shape.SetFilterData(filter);
		}else{
			filter = boxes[i].shape.GetFilterData();
			if (filter.groupIndex != -1){
				filter.groupIndex = -1;
				boxes[i].shape.SetFilterData(filter);
			}
		}
	}
}

var key:Object = new Object();
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyPressed);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyReleased);
function onKeyPressed(evt:KeyboardEvent):void{ key[evt.keyCode] = true; }
function onKeyReleased(evt:KeyboardEvent):void{ key[evt.keyCode] = false; }

NOTE: This requires the QuickBox2D mini-library

This snippet shows how to use groupIndex to create simple platforms for a character to jump on. A few people have asked me about this so I figured I’d post about it. It works by altering the groupIndex of each platform based on the characters position – if the character is below a platform, both the character and the platform have groupIndex -1 (meaning they will not collide). As soon as the character is above a platform, the platforms groupIndex is set to 1 (allowing the character to walk on the platform despite having just passed through it.


Have a look at the swf…


It’s also worth noting that I’m using b2World.GetContactCount() to make sure it’s ok to jump… if there were more going on in the demo you’d need to check a specific contact point(s), but since the only collision that could happen involves the character this works nicely…

Read snippet: QuickBox2D groupIndex Platforms

QuickBox2D Unicycler

import com.actionsnippet.qbox.*;
import Box2D.Common.Math.*;
import Box2D.Dynamics.Joints.*;
[SWF (backgroundColor=0x000000, width=700, height=600, frameRate=60)]
var sim:QuickBox2D = new QuickBox2D(this, {debug:false,gravityY:10, renderJoints:false});
sim.setDefault({fillColor:0xCC0000,lineAlpha:0});
sim.createStageWalls();

for (var i:int = 0; i<20; i++){
	sim.addCircle({x:1+i * 0.8, y:1, radius:0.1+Math.random()*0.1, restitution:0.3});
}

sim.setDefault({fillColor:0xEFEFEF, fillAlpha:0.5, lineColor:0xFFFFFF, groupIndex:-2});
var ancA:QuickObject = sim.addCircle({x:20, y:12, radius:0.5, density:0, skin:"none"});
var ancB:QuickObject = sim.addCircle({x:20, y:2, radius:0.5, density:0, skin:"none"});
var head:QuickObject = sim.addCircle({x:0, y:-0.6, radius:0.5});
var neck:QuickObject = sim.addBox({x:0, y:1, width:0.5, height:2});
var spindle:QuickObject = sim.addBox({x:0, y:3, width:0.1, height:2})
spindle.userData.visible = false;
var top:QuickObject = sim.addGroup({objects:[head, neck, spindle], x:10, y:5, fixedRotation:true});
var upperLegA:QuickObject = sim.addBox({x:10, y:7.5, width:0.25, height:1.5});
var lowerLegA:QuickObject = sim.addBox({x:10, y:9, width:0.25, height:1.5});
var wheel:QuickObject = sim.addCircle({x:10, y:9, radius:1});
var upperLegB:QuickObject = sim.addBox({x:10, y:7.5, width:0.25, height:1.5});
var lowerLegB:QuickObject = sim.addBox({x:10, y:9, width:0.25, height:1.5});
var upperArmA:QuickObject = sim.addBox({x:10.7, y:5.25, width:1.5, height:0.25});
var lowerArmA:QuickObject = sim.addBox({x:11.8, y:5.25, width:1.2, height:0.25});
var upperArmB:QuickObject = sim.addBox({x:9.5, y:5.25, width:1.5, height:0.25});
var lowerArmB:QuickObject = sim.addBox({x:8.4, y:5.25, width:1.2, height:0.25});
var anchor:b2Vec2 = new b2Vec2();
function connect(a:QuickObject, b:QuickObject, lower:Number, upper:Number, offX:Number=0, offY:Number = 0):QuickObject{
	 var min:Number = Math.min(a.y, b.y);
     var max:Number = Math.max(a.y, b.y);
     anchor.y = min + (max - min) * 0.5 + offY;
     min = Math.min(a.x, b.x);
     max = Math.max(a.x, b.x);
     anchor.x = min + (max - min) * 0.5 + offX;
	 return sim.addJoint({type:"revolute", a:a.body, b:b.body, x1:anchor.x, y1:anchor.y, lowerAngle:lower, upperAngle:upper});
}
sim.addJoint({type:"revolute", a:wheel.body, b:top.body, x1:wheel.x, y1:wheel.y});
sim.addJoint({type:"revolute", a:lowerLegA.body, b:wheel.body, x1:lowerLegA.x, y1:lowerLegA.y + 0.7});
sim.setDefault({enableLimit:true});
connect(top, upperArmA, 0, 3, -.3, .1);
connect(upperArmA, lowerArmA, -2.5, -1, 0, 0);
connect(top, upperArmB, -3, 0, .3, .1);
connect(upperArmB, lowerArmB, -2.5, -1, 0, 0);
sim.addJoint({type:"distance", a:lowerArmA.body, b:ancA.body, frequencyHz:0.3});
sim.addJoint({type:"distance", a:lowerArmB.body, b:ancB.body, frequencyHz:0.3});
sim.setDefault({enableLimit:true});
connect(top, upperLegA, -6.28, 6, 0, 0.8);
connect(upperLegA, lowerLegA, .1, 6);
wheel.angle = Math.PI;
connect(top, upperLegB, -6.28, 6, 0, 0.8);
connect(upperLegB, lowerLegB, .1, 6);
sim.setDefault({enableLimit:false, type:"revolute"});
sim.addJoint({a:lowerLegB.body, b:wheel.body, x1:lowerLegB.x, y1:lowerLegB.y + 0.7});

sim.start();
sim.mouseDrag();

var sd:Number = 5, s:Number = 0, t:Number = 0;
addEventListener(Event.ENTER_FRAME, function():void{
	top.angle = Math.cos(t += 0.05) * 0.2;
    if (top.x > 19) sd = -5
	if (top.x < 5) sd = 5;
	s += (sd - s) / 8;
	wheel.body.SetAngularVelocity(s);
});

This chunk of code uses QuickBox2D to create a simple looking character riding a unicycle…

Have a look at the swf…

Read snippet: QuickBox2D Unicycler

QuickBox2D Floating Walker

import com.actionsnippet.qbox.*;
import Box2D.Common.Math.*;
import Box2D.Dynamics.Joints.*;

[SWF (backgroundColor=0x000000, width=700, height=600, frameRate=60)]

var sim:QuickBox2D = new QuickBox2D(this, {gravityY:.2});

sim.setDefault({fillColor:0xEFEFEF, fillAlpha:0.5, lineColor:0xFFFFFF});

sim.createStageWalls();

for (var i:int = 0; i<20; i++){
	sim.addCircle({x:1+i, y:1, radius:0.2, restitution:1});
}

var head:QuickObject = sim.addCircle({x:0, y:0, radius:0.5});
var neck:QuickObject = sim.addBox({x:0, y:1, width:0.5, height:2});
var top:QuickObject = sim.addGroup({objects:[head, neck], x:10, y:5, fixedRotation:true});

var upperLegA:QuickObject = sim.addBox({x:10, y:7.5, width:0.25, height:1.5,groupIndex:-2});
var lowerLegA:QuickObject = sim.addBox({x:10, y:9, width:0.25, height:1.5,groupIndex:-2});

var upperLegB:QuickObject = sim.addBox({x:10, y:7.5, width:0.25, height:1.5,groupIndex:-2});
var lowerLegB:QuickObject = sim.addBox({x:10, y:9, width:0.25, height:1.5,groupIndex:-2});

var anchor:b2Vec2 = new b2Vec2();
function connect(a:QuickObject, b:QuickObject, lower:Number, upper:Number, offX:Number=0, offY:Number = 0):QuickObject{
	 var min:Number = Math.min(a.y, b.y);
     var max:Number = Math.max(a.y, b.y);
     anchor.y = min + (max - min) * 0.5 + offY;
     min = Math.min(a.x, b.x);
     max = Math.max(a.x, b.x);
     anchor.x = min + (max - min) * 0.5 + offX;
	 return sim.addJoint({a:a.body, b:b.body, x1:anchor.x, y1:anchor.y, lowerAngle:lower, upperAngle:upper});
}

sim.setDefault({type:"revolute", collideConnected:false, enableLimit:true, enableMotor:true, maxMotorTorque:250, lineColor:0xFFFFFF});

var upperLegAJoint:QuickObject = connect(top, upperLegA, -1, 1, 0, 0.8);
var upperLegBJoint:QuickObject = connect(top, upperLegB, -1, 1, 0, 0.8);

var lowerLegAJoint:QuickObject = connect(upperLegA, lowerLegA, -1, 1);
var lowerLegBJoint:QuickObject = connect(upperLegB, lowerLegB, -1, 1);

sim.start();
sim.mouseDrag();
 

var j:b2RevoluteJoint;
var t:Number = -Math.PI * 0.5;
addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
	
	var s1:Number = 1*Math.sin(t);
	var s2:Number = 1*Math.sin(t+Math.PI);
	t+= 0.05;
	
	top.body.SetAngularVelocity(s1 * 0.25);
	
	j =  upperLegAJoint.joint as b2RevoluteJoint;
    j.SetMotorSpeed(s1);
	
	j =  upperLegBJoint.joint as b2RevoluteJoint;
    j.SetMotorSpeed(s2);
	 
	j = lowerLegAJoint.joint as b2RevoluteJoint;
	j.SetMotorSpeed(-s2);
	
	j = lowerLegBJoint.joint as b2RevoluteJoint;
	j.SetMotorSpeed(-s1);
}

Experimenting with primitive robot designs… this was just an improvised design – it looks like it may make sense to break out a pencil and some paper – or my wacom… to get something interesting…


Have a look at the swf…


Read snippet: QuickBox2D Floating Walker