Created
April 16, 2010 05:06
-
-
Save lamberta/368032 to your computer and use it in GitHub Desktop.
PaperBox2D.as demo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* scenes/Box2dScene.as | |
*/ | |
package scenes { | |
import Box2D.Collision.Shapes.b2PolygonDef; | |
import Box2D.Collision.b2AABB; | |
import Box2D.Common.Math.b2Vec2; | |
import Box2D.Dynamics.Joints.b2MouseJoint; | |
import Box2D.Dynamics.Joints.b2MouseJointDef; | |
import Box2D.Dynamics.b2Body; | |
import Box2D.Dynamics.b2BodyDef; | |
import Box2D.Dynamics.b2DebugDraw; | |
import Box2D.Dynamics.b2World; | |
import flash.display.Sprite; | |
import org.papervision3d.objects.DisplayObject3D; | |
public class Box2dScene extends Sprite { | |
private var worldWidth:Number; | |
private var worldHeight:Number; | |
private var iterations:uint; | |
private var timestep:Number; | |
private var worldScale:uint; | |
private var world:b2World; | |
private var mouseXWorldPhys:Number; | |
private var mouseYWorldPhys:Number; | |
private var mouseXWorld:Number; | |
private var mouseYWorld:Number; | |
private var mouseJoint:b2MouseJoint; | |
private var _isMouseDown:Boolean = false; | |
private var draggedBody:b2Body; | |
private var box2dBodies:Array; | |
private var _arrayPos:int = -1; | |
public function Box2dScene(w:Number, h:Number, iterate:uint) { | |
initVars(w, h, iterate); | |
initB2World(); | |
createB2Walls(); | |
} | |
private function initVars(w:Number, h:Number, iterate:uint):void { | |
worldWidth = w; | |
worldHeight = h; | |
iterations = iterate; | |
timestep = 1/30; | |
worldScale = 50; | |
} | |
/** | |
* Initialize the box2d world. | |
*/ | |
private function initB2World():void { | |
var worldBounds:b2AABB = new b2AABB(); | |
worldBounds.lowerBound.Set(0, 0); | |
worldBounds.upperBound.Set(worldWidth/worldScale, worldHeight/worldScale); | |
var gravity:b2Vec2 = new b2Vec2(0, 10); | |
var sleepMode:Boolean = true; | |
world = new b2World(worldBounds, gravity, sleepMode); | |
} | |
/** | |
* Create boundries around the stage with a border of boxes.. | |
*/ | |
private function createB2Walls():void { | |
var wallShapeDef:b2PolygonDef = new b2PolygonDef(); | |
var wallBodyDef:b2BodyDef = new b2BodyDef(); | |
var wall:b2Body; | |
//Left and Right Shape Definition | |
wallShapeDef.SetAsBox(100/worldScale, (worldHeight + 40)/worldScale/2); | |
// Left | |
wallBodyDef.position.Set(-95/worldScale, worldHeight/worldScale/2); | |
wall = world.CreateBody(wallBodyDef); | |
wall.CreateShape(wallShapeDef); | |
// Right | |
wallBodyDef.position.Set((worldWidth+95)/worldScale, worldHeight/worldScale/2); | |
wall = world.CreateBody(wallBodyDef); | |
wall.CreateShape(wallShapeDef); | |
//Top and bottom shape definition | |
wallShapeDef.SetAsBox((worldWidth+40)/worldScale/2, 100/worldScale); | |
// Top | |
wallBodyDef.position.Set(worldWidth/worldScale/2, -95/worldScale); | |
wall = world.CreateBody(wallBodyDef); | |
wall.CreateShape(wallShapeDef); | |
// Bottom | |
wallBodyDef.position.Set(worldWidth/worldScale/2, (worldHeight+95)/worldScale); | |
wall = world.CreateBody(wallBodyDef); | |
wall.CreateShape(wallShapeDef); | |
wall.SetMassFromShapes(); | |
} | |
/** | |
* Create box2d bodies from passed pv3d objects. | |
*/ | |
public function createBodiesFrom3dObjects(pv3dObjects:Array):void { | |
box2dBodies = new Array(); | |
for(var i:uint; i < pv3dObjects.length; i++) { | |
var do3d:DisplayObject3D = pv3dObjects[i] as DisplayObject3D; | |
var bodyDef:b2BodyDef = new b2BodyDef(); | |
bodyDef.allowSleep = true; | |
//align box2d shapes to pv3d object (0,0) in middle of 3d | |
bodyDef.position = new b2Vec2((do3d.x + worldWidth/2) / worldScale, | |
(do3d.y + worldHeight/2) / worldScale); | |
var width:Number = do3d.extra.width; | |
var height:Number = do3d.extra.height; | |
var boxShape:b2PolygonDef = new b2PolygonDef(); | |
boxShape.SetAsBox(width/worldScale, height/worldScale); | |
boxShape.density = .7; | |
boxShape.friction = 6; | |
boxShape.restitution = .1; | |
var body:b2Body = world.CreateBody(bodyDef); | |
body.CreateShape(boxShape); | |
body.SetUserData(do3d); | |
body.SetMassFromShapes(); | |
box2dBodies.push(body); | |
} | |
} | |
/** | |
* Step through Box2D simulation. | |
* Called from render-loop. | |
*/ | |
public function step():void { | |
updateMouseWorld(); | |
mouseDrag(); | |
world.Step(timestep, iterations); | |
//move the assigned pv3d object from the new box2d position | |
for(var bb:b2Body = world.GetBodyList(); bb; bb = bb.GetNext()) { | |
if(bb.GetUserData() is DisplayObject3D) { | |
bb.GetUserData().x = bb.GetPosition().x * worldScale - worldWidth / 2; | |
bb.GetUserData().y = -bb.GetPosition().y * worldScale + worldHeight / 2; | |
bb.GetUserData().rotationZ = -bb.GetAngle() * (180 / Math.PI); | |
} | |
} | |
} | |
private function updateMouseWorld():void { | |
mouseXWorld = mouseX; | |
mouseYWorld = mouseY; | |
mouseXWorldPhys = mouseX/worldScale; | |
mouseYWorldPhys = mouseY/worldScale; | |
} | |
private function mouseDrag():void{ | |
// mouse press | |
if(_isMouseDown && !mouseJoint){ | |
draggedBody = null; | |
if(_arrayPos > -1){ | |
//negative number means nothing selected | |
draggedBody = box2dBodies[_arrayPos]; | |
} | |
if(draggedBody) { | |
var md:b2MouseJointDef = new b2MouseJointDef(); | |
md.body1 = world.GetGroundBody(); | |
md.body2 = draggedBody; | |
md.target.Set(mouseXWorldPhys, mouseYWorldPhys); | |
md.maxForce = 300.0 * draggedBody.GetMass(); | |
md.timeStep = timestep; | |
mouseJoint = world.CreateJoint(md) as b2MouseJoint; | |
draggedBody.WakeUp(); | |
} | |
} | |
//mouse release | |
if(!_isMouseDown){ | |
if(mouseJoint){ | |
world.DestroyJoint(mouseJoint); | |
mouseJoint = null; | |
} | |
} | |
//mouse move | |
if(mouseJoint){ | |
var p2:b2Vec2 = new b2Vec2(mouseXWorldPhys, mouseYWorldPhys); | |
mouseJoint.SetTarget(p2); | |
} | |
} | |
/** | |
* getter's and setter's | |
**/ | |
public function get isMouseDown():Boolean { | |
return _isMouseDown; | |
} | |
public function set isMouseDown(mousedown:Boolean):void { | |
_isMouseDown = mousedown; | |
} | |
public function set arrayPos(i:int):void { | |
_arrayPos = i; | |
} | |
public function get debugDrawSprite():Sprite { | |
var debugDraw:b2DebugDraw = new b2DebugDraw(); | |
debugDraw.SetSprite(new Sprite()); | |
/** debug draw flags: | |
* DebugDraw.e_aabbBit, DebugDraw.e_centerOfMassBit, DebugDraw.e_coreShapeBit, | |
* DebugDraw.e_obbBit, DebugDraw.e_pairBit, b2DebugDraw.e_shapeBit, | |
* b2DebugDraw.e_jointBit | |
**/ | |
debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit); | |
debugDraw.SetDrawScale(worldScale); | |
debugDraw.SetFillAlpha(0.25); | |
debugDraw.SetLineThickness(1); | |
world.SetDebugDraw(debugDraw); | |
return debugDraw.GetSprite(); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Papervision3D + Box2D Demo. | |
* http://lamberta.posterous.com/papervision3d-box2d | |
* | |
* Compiled with: | |
* Papervision3D Public Beta 2.0, build 804. | |
* Box2DAS3 2.0.1, build 23. | |
* | |
* This software is released under the MIT License. | |
* Copyright (c) Billy Lamberta 2008, <www.lamberta.org> | |
*/ | |
package { | |
import com.adobe.viewsource.ViewSource; | |
import flash.display.Sprite; | |
import flash.display.StageAlign; | |
import flash.display.StageQuality; | |
import flash.display.StageScaleMode; | |
import flash.events.Event; | |
import flash.events.KeyboardEvent; | |
import flash.events.MouseEvent; | |
import flash.text.TextField; | |
import flash.text.TextFieldAutoSize; | |
import scenes.Box2dScene; | |
import scenes.Pv3dScene; | |
import scenes.events.Pv3dArrayEvent; | |
[SWF(backgroundColor="0x666666", framerate="30")] | |
public class PaperBox2D extends Sprite { | |
private var stageWidth:Number; | |
private var stageHeight:Number; | |
private var pv3dScene:Pv3dScene; | |
private var box2dScene:Box2dScene; | |
private static const BOX2D_ITERATIONS:uint = 10; //simulation quality | |
private var box2dDebug:Sprite; | |
public function PaperBox2D() { | |
stageSetup(); | |
init3d(); | |
initBox2d(); | |
//add render-loop | |
this.addEventListener(Event.ENTER_FRAME, onEnterFrame, false, 0, true); | |
setupText(); | |
} | |
/** | |
* Setup stage. | |
*/ | |
private function stageSetup():void { | |
stageWidth = stage.stageWidth; | |
stageHeight = stage.stageHeight; | |
stage.quality = StageQuality.MEDIUM; | |
stage.align = StageAlign.TOP_LEFT; | |
stage.scaleMode = StageScaleMode.NO_SCALE; | |
stage.addEventListener(KeyboardEvent.KEY_DOWN, debugDraw, false, 0, true); | |
ViewSource.addMenuItem(this, "srcview/index.html"); | |
} | |
/** | |
* Papervision3D setup. | |
*/ | |
private function init3d():void { | |
//setup pv3d | |
pv3dScene = new Pv3dScene(stageWidth, stageHeight); | |
pv3dScene.addEventListener(Event.ADDED_TO_STAGE, setupPv3dEvents, false, 0, true); | |
this.addChild(pv3dScene); | |
} | |
private function setupPv3dEvents(event:Event):void { | |
//dispatched internally | |
pv3dScene.addEventListener(Pv3dArrayEvent.OBJECT_PRESS, on3dObjPress); | |
} | |
/** | |
* Box2D setup. | |
*/ | |
private function initBox2d():void { | |
//setup box2d | |
box2dScene = new Box2dScene(stageWidth, stageHeight, BOX2D_ITERATIONS); | |
box2dScene.addEventListener(Event.ADDED_TO_STAGE, setupBox2dEvents, false, 0, true); | |
this.addChild(box2dScene); | |
//creates box2d bodies based on dimensions of the pv3d objects | |
box2dScene.createBodiesFrom3dObjects(pv3dScene.createShapes()); | |
//do once to get pv3d objects in place | |
box2dScene.step(); | |
//debug draw | |
box2dDebug = box2dScene.debugDrawSprite; | |
box2dDebug.visible = false; | |
addChild(box2dDebug); | |
} | |
private function setupBox2dEvents(event:Event):void { | |
//attach mouse listeners to stage | |
this.stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown, false, 0, true); | |
this.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp, false, 0, true); | |
} | |
/** | |
* Mouse handlers for object selection. | |
*/ | |
private function on3dObjPress(event:Pv3dArrayEvent):void { | |
//set the box2d array position so it knows what shape to move | |
box2dScene.arrayPos = event.arrayPos; | |
} | |
private function onMouseDown(event:MouseEvent):void { | |
box2dScene.isMouseDown = true; | |
} | |
private function onMouseUp(event:MouseEvent):void { | |
box2dScene.isMouseDown = false; | |
box2dScene.arrayPos = -1; //reset body selected | |
} | |
/** | |
* Toggle box2d debug draw. | |
*/ | |
private function debugDraw(event:KeyboardEvent):void { | |
//listen for 'd' | |
if(event.keyCode == 68) { | |
if(box2dDebug.visible) { | |
box2dDebug.visible = false; | |
}else { | |
box2dDebug.visible = true; | |
} | |
} | |
} | |
/** | |
* render-loop. | |
*/ | |
private function onEnterFrame(event:Event):void { | |
pv3dScene.updateCamera(); | |
pv3dScene.render(); | |
box2dScene.step(); | |
} | |
private function setupText():void { | |
var text:TextField = new TextField(); | |
text.autoSize = TextFieldAutoSize.LEFT; | |
text.multiline = true; | |
text.textColor = 0xFFFFFF; | |
text.x = 20; | |
text.y = 10; | |
text.htmlText = "Papervision3D + Box2D." + | |
"<br><a href='http://www.lamberta.org/blog/PaperBox2D'>www.lamberta.org/blog/paperbox2d</a>" + | |
"<br><br>Press 'd' to toggle the Box2D debug regions." + | |
"<br>Right-click to view source."; | |
this.addChild(text); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* scenes/events/Pv3dArrayEvent.as | |
*/ | |
/** | |
* Broadcasts an object's position in an array. | |
**/ | |
package scenes.events { | |
import flash.events.Event; | |
public class Pv3dArrayEvent extends Event { | |
public static const OBJECT_PRESS:String = "onObjectPress"; | |
public static const OBJECT_RELEASE:String = "onObjectRelease"; | |
//object position in the array, negative for nothing selected | |
private var _arrayPos:int; | |
public function Pv3dArrayEvent(type:String, arrayPos:int) { | |
this._arrayPos = arrayPos; | |
super(type); | |
} | |
public function get arrayPos():int { | |
return _arrayPos; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* scenes/Pv3dScene.as | |
*/ | |
package scenes { | |
import flash.display.Sprite; | |
import org.papervision3d.cameras.Camera3D; | |
import org.papervision3d.events.InteractiveScene3DEvent; | |
import org.papervision3d.lights.PointLight3D; | |
import org.papervision3d.materials.shadematerials.FlatShadeMaterial; | |
import org.papervision3d.materials.utils.MaterialsList; | |
import org.papervision3d.objects.DisplayObject3D; | |
import org.papervision3d.objects.primitives.Cube; | |
import org.papervision3d.render.BasicRenderEngine; | |
import org.papervision3d.scenes.Scene3D; | |
import org.papervision3d.view.Viewport3D; | |
import scenes.events.Pv3dArrayEvent; | |
public class Pv3dScene extends Sprite { | |
private var stageWidth:Number; | |
private var stageHeight:Number; | |
//pv3d vars | |
private var viewport:Viewport3D; | |
private var scene:Scene3D; | |
private var renderer:BasicRenderEngine; | |
private var camera:Camera3D; | |
public function Pv3dScene(w:Number, h:Number) { | |
initStage(w, h); | |
init3d(); | |
setupScene(); | |
} | |
private function initStage(w:Number, h:Number):void { | |
stageWidth = w; | |
stageHeight = h; | |
} | |
private function init3d():void { | |
viewport = new Viewport3D(stageWidth, stageHeight, true, true); | |
this.addChild(viewport); | |
renderer = new BasicRenderEngine(); | |
scene = new Scene3D(); | |
camera = new Camera3D(); | |
} | |
private function setupScene():void { | |
//setup camera start settings | |
camera.zoom = 1000 / camera.focus + 1; | |
//camera target | |
var cameraTarget:DisplayObject3D = new DisplayObject3D(); | |
camera.target = cameraTarget; | |
} | |
/** | |
* called from above. | |
* creates shape dimensions from pv3d objects to pass on to box2d. | |
*/ | |
public function createShapes():Array { | |
var shapesDimensions:Array = new Array() | |
addCubeShapes(shapesDimensions); | |
return shapesDimensions; | |
} | |
private function addCubeShapes(shapesDimensions:Array, boxes:uint = 4):void { | |
var width:Number = 50; | |
var height:Number = 50; | |
var depth:Number = 50; | |
//flatshade light | |
var light:PointLight3D = new PointLight3D(true); | |
light.z = -100; | |
//create materials for boxes | |
for(var i:uint = 0; i < boxes; i++) { | |
//material | |
var randomColor:uint = Math.random() * 0xFFFFFF; | |
var flatShadeMaterial:FlatShadeMaterial = new FlatShadeMaterial(light, randomColor);; | |
flatShadeMaterial.interactive = true; | |
var matList:MaterialsList = new MaterialsList();; | |
matList.addMaterial(flatShadeMaterial, "all"); | |
var cube:Cube = new Cube(matList, width * 2, depth * 2, height * 2, 2, 2, 2); | |
//store extras we will access in the box2dscene | |
cube.extra = {width:width, height:height, arrayPos:i}; | |
//will position obects in pv3d, box2d will follow | |
cube.x = (i * 150) - 200; | |
cube.y = 150; //move down a bit, y is reversed because of box2d | |
//add listener to pv3d object | |
cube.addEventListener(InteractiveScene3DEvent.OBJECT_PRESS, on3dObjPress, false, 0, true); | |
//add to pv3d | |
scene.addChild(cube); | |
//push into array that will go to box2d | |
shapesDimensions.push(cube); | |
} | |
} | |
/** | |
* Added to 3d object, broadcasts it's array position on click. | |
*/ | |
private function on3dObjPress(event:InteractiveScene3DEvent):void { | |
this.dispatchEvent(new Pv3dArrayEvent(Pv3dArrayEvent.OBJECT_PRESS, event.target.extra.arrayPos)); | |
} | |
/** | |
* called from render-loop. | |
*/ | |
public function render():void { | |
renderer.renderScene(scene, camera, viewport); | |
} | |
/** | |
* Camera hovering. | |
*/ | |
public function updateCamera():void { | |
camera.x -= (camera.x - viewport.containerSprite.mouseX * 5) / 200; | |
camera.y -= (camera.y - viewport.containerSprite.mouseY * 5) / 200; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment