以前研究魔术笔的游戏,今日心血来潮仿制了一个pv3d和box2d的小程序,按下鼠标左键右拖动绘制盒子,按下左拖动绘制球,还比较有意思。完整源码下载
box2d: http://box2dflash.sourceforge.net/
papervision3d: http://www.papervision3d.org
文件较大,耐心等待...
代码如下:
package
{
import Box2D.Collision.Shapes.b2CircleDef;
import Box2D.Collision.Shapes.b2PolygonDef;
import Box2D.Collision.Shapes.b2Shape;
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 General.FpsCounter;
import General.Input;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.utils.getTimer;
import org.papervision3d.core.effects.view.ReflectionView;
import org.papervision3d.lights.PointLight3D;
import org.papervision3d.materials.shadematerials.GouraudMaterial;
import org.papervision3d.materials.utils.MaterialsList;
import org.papervision3d.objects.DisplayObject3D;
import org.papervision3d.objects.primitives.Cube;
import org.papervision3d.objects.primitives.Sphere;
[SWF(width="600", height="600", backgroundColor="#000000", frameRate="40")]
public class DrawBox extends ReflectionView
{
public var w:int = 600,h:int = 600;//视野的宽和高
public const scale:Number = 30;//缩放比率,box2d中1px = 30m
protected var iterations:int = 10,timeStep:Number = 1/30;
protected var world:b2World;
protected var mouseXWorldPhys:Number,mouseYWorldPhys:Number;
protected var isDrawing:Boolean = false,isMouseDown:Boolean = false;
protected var clickX:Number,clickY:Number;//点击开始点
protected var drawSprite:Sprite = new Sprite();
protected var materialsList:MaterialsList;
protected var light:PointLight3D = new PointLight3D(true);//灯光
protected var gmaterials:GouraudMaterial ;
static public var m_fpsCounter:FpsCounter = new FpsCounter();
public function DrawBox()
{
//Fps
m_fpsCounter.x = 7;
m_fpsCounter.y = 5;
addChildAt(m_fpsCounter, 0);
//绘制容器
addChild(drawSprite);
super(this.w,this.h,true,false,"CAMERA3D");
//以舞台正中间的为起点
surfaceHeight = -this.h + 110;
light.x = 1000;light.y = 1000;light.z = -1000;
this.camera.focus = 10;this.camera.zoom = 100;
this.gmaterials = new GouraudMaterial(light,0xcc0000,0x111111,10);
this.materialsList = new MaterialsList({all : gmaterials});
//初始包围盒
var b2aabb:b2AABB = new b2AABB();
b2aabb.lowerBound.Set(0, 0);
b2aabb.upperBound.Set(this.w / this.scale, this.h / this.scale);
//重力
var g:b2Vec2 = new b2Vec2(0, 10);
//2d物理世界
this.world = new b2World(b2aabb, g, true);
//debug绘制
//this.setupDebugDraw();
//地面
this.createFloor();
for(var i:int = 0; i < 2;i++){
this.addCube(Math.random()*20,Math.random()*20,Math.random()*60,Math.random()*60);
this.addSphere(Math.random()*60,Math.random()*20,Math.random()*30);
}
//开始3d的渲染
this.startRendering();
//事件初始
stage.addEventListener(Event.ENTER_FRAME, this.enterFrame);
stage.addEventListener(MouseEvent.MOUSE_DOWN, this.onMouseDown);
stage.addEventListener(MouseEvent.MOUSE_MOVE, this.onMouseMove);
stage.addEventListener(MouseEvent.MOUSE_UP, this.onMouseUp);
}
//创建所有墙体
private function createFloor() : void{
//下
createBox(this.w / this.scale / 2, (this.h + 5) / this.scale ,this.w / this.scale / 2, 10 / this.scale);
//左
createBox(-5 / this.scale, this.h / this.scale / 2,10 / this.scale, this.h / this.scale / 2);
//右
createBox(this.w / this.scale, this.h / this.scale / 2,10 / this.scale, this.h / this.scale / 2);
}
//添加3d盒子
public function addCube(x:Number,y:Number,bw:Number,bh:Number):void{
var body:b2Body = createBox((x + bw / 2) / this.scale, (y + bh / 2) / this.scale ,bw / 2 / this.scale, bh / 2 / this.scale,false);
var cube:Cube = new Cube(this.materialsList, bw, (bw + bh) / 2, bh);
scene.addChild(cube);
body.m_userData = cube;
}
//添加3d球体
public function addSphere(x:Number,y:Number,radius:Number):void{
var body:b2Body = createCircle(x / this.scale, y / this.scale , radius ,false);
var sphere:Sphere = new Sphere(this.gmaterials, radius);
scene.addChild(sphere);
body.m_userData = sphere;
}
//矩形
private function createBox(x:Number,y:Number,hx:Number,hy:Number,isSleep:Boolean = true):b2Body{
var def:b2PolygonDef = new b2PolygonDef();
if(isSleep == false){
def.density = 1;
def.friction = 0.7;
def.restitution = 0.7;
}
var bodyDef:b2BodyDef = new b2BodyDef();
bodyDef.position.Set(x,y);
def.SetAsBox(hx,hy);
var body:b2Body = this.world.CreateBody(bodyDef);
body.CreateShape(def);
body.SetMassFromShapes();
return body;
}
//球
private function createCircle(x:Number,y:Number,radius:Number,isSleep:Boolean = true):b2Body{
var def:b2CircleDef = new b2CircleDef();
if(isSleep == false){
def.density = 1;
def.friction = 0.7;
def.restitution = 0.7;
}
def.radius = radius / this.scale
var bodyDef:b2BodyDef = new b2BodyDef();
bodyDef.position.Set(x,y);
var body:b2Body = this.world.CreateBody(bodyDef);
body.CreateShape(def);
body.SetMassFromShapes();
return body;
}
//debug绘制刚体等外观轮廓
public function setupDebugDraw() : void {
var dbg:Sprite = new Sprite();
addChild(dbg);
var dd:b2DebugDraw = new b2DebugDraw();
dd.m_sprite = dbg;
dd.m_drawScale = this.scale;
dd.m_fillAlpha = 0.5;
dd.m_lineThickness = 1;
dd.m_drawFlags = b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit | b2DebugDraw.e_centerOfMassBit;
this.world.SetDebugDraw(dd);
}
//2d物理世界主循环
public function enterFrame(evt:Event) : void{
var physStart:uint = getTimer();
this.updateMouseWorld();
this.mouseDrag();
this.world.Step(this.timeStep, this.iterations);
var d3d:DisplayObject3D;
for (var bb:b2Body = world.m_bodyList; bb; bb = bb.m_next){
if (bb.m_userData is DisplayObject3D){
d3d = bb.m_userData as DisplayObject3D;
d3d.x = bb.GetPosition().x * this.scale - this.w * 0.5;
d3d.y = (-bb.GetPosition().y) * this.scale + this.h * 0.5 + 50;
d3d.rotationZ = (-bb.GetAngle()) * (180 / Math.PI);
}
}
this.singleRender();
m_fpsCounter.update();
m_fpsCounter.updatePhys(physStart);
}
public function updateMouseWorld() : void{
this.mouseXWorldPhys = mouseX / this.scale;
this.mouseYWorldPhys = mouseY / this.scale;
}
//开始拖动
public function onMouseDown(evt:MouseEvent) : void{
this.clickX = mouseX;
this.clickY = mouseY;
this.isDrawing = true;
this.isMouseDown = true;
}
//拖动绘制
public function onMouseMove(evt:MouseEvent) : void{
if (this.isDrawing){
with(drawSprite){
graphics.clear();
graphics.beginFill(0xFF0000, 0.5);
if (this.clickX > mouseX){
graphics.drawCircle(mouseX, mouseY, Math.min(40, Point.distance(new Point(this.clickX, this.clickY), new Point(mouseX, mouseY)) / 2));
}else{
graphics.drawRect(clickX,clickY, Math.min(200, mouseX - this.clickX), Math.min(100, mouseY - this.clickY));
}
}
this.isMouseDown = false;
}
}
//拖动结束
public function onMouseUp(evt:MouseEvent) : void{
if (this.isDrawing){
if (this.clickX > mouseX){//向左画圆
this.addSphere(mouseX, mouseY,drawSprite.width/2);
}else{//向右画方
this.addCube(clickX,clickY,drawSprite.width,drawSprite.height);
}
drawSprite.graphics.clear();
this.isDrawing = false;
}
this.isMouseDown = false;
}
//拖动选中的3d对象
public var m_mouseJoint:b2MouseJoint;
public function mouseDrag():void{
// mouse press
if (isMouseDown && !m_mouseJoint){
var body:b2Body = GetBodyAtMouse();
if (body){
this.isDrawing = false;
drawSprite.graphics.clear();
var md:b2MouseJointDef = new b2MouseJointDef();
md.body1 = world.GetGroundBody();
md.body2 = body;
md.target.Set(mouseXWorldPhys, mouseYWorldPhys);
md.maxForce = 300.0 * body.GetMass();
md.timeStep = timeStep;
m_mouseJoint = world.CreateJoint(md) as b2MouseJoint;
body.WakeUp();
}
}
// mouse release
if (!isMouseDown){
if (m_mouseJoint){
world.DestroyJoint(m_mouseJoint);
m_mouseJoint = null;
}
}
// mouse move
if (m_mouseJoint){
m_mouseJoint.SetTarget(new b2Vec2(mouseXWorldPhys, mouseYWorldPhys));
}
}
//按d键删除选中的3d对象
public function MouseDestroy():void{
// mouse press
if (!this.isMouseDown && Input.isKeyPressed(68/*D*/)){
var body:b2Body = GetBodyAtMouse(true);
if (body){
world.DestroyBody(body);
return;
}
}
}
//取当前鼠标下刚体
private var mousePVec:b2Vec2 = new b2Vec2();
public function GetBodyAtMouse(includeStatic:Boolean=false):b2Body{
// Make a small box.
mousePVec.Set(mouseXWorldPhys, mouseYWorldPhys);
var aabb:b2AABB = new b2AABB();
aabb.lowerBound.Set(mouseXWorldPhys - 0.001, mouseYWorldPhys - 0.001);
aabb.upperBound.Set(mouseXWorldPhys + 0.001, mouseYWorldPhys + 0.001);
// Query the world for overlapping shapes.
var k_maxCount:int = 10;
var shapes:Array = new Array();
var count:int = world.Query(aabb, shapes, k_maxCount);
var body:b2Body = null;
for (var i:int = 0; i < count; ++i)
{
//if (shapes[i].GetBody().IsStatic() == false || includeStatic)
{
var tShape:b2Shape = shapes[i] as b2Shape;
var inside:Boolean = tShape.TestPoint(tShape.GetBody().GetXForm(), mousePVec);
if (inside){
body = tShape.GetBody();
break;
}
}
}
return body;
}
}
}