新选组 发表于 2012-5-3 09:38:13

论述如何基于Box2D模拟星球重力效果

随着《Angry Birds Space》的问世,我想你定非常疑惑要如何通过Box2D模拟星球重力。基本原理非常简单。首先,太空没有重力,所以你将通过如下方式创建没有重力的b2World世界:private var world:b2World=new b2World(new b2Vec2(0,0),true);abspace from emanueleferonato.com接着就是根据主体和球体位置落实Force。参考如下脚本:package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import Box2D.Dynamics.*;
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Common.Math.*;
import Box2D.Dynamics.Joints.*;
public class Main extends Sprite {
private var world:b2World=new b2World(new b2Vec2(0,0),true);
private var worldScale:Number=30;
private var planetVector:Vector.<b2Body>=new Vector.<b2Body>();
private var debrisVector:Vector.<b2Body>=new Vector.<b2Body>();
private var orbitCanvas:Sprite=new Sprite();
public function Main() {
addChild(orbitCanvas);
orbitCanvas.graphics.lineStyle(1,0xff0000);
debugDraw();
addPlanet(180,240,90);
addPlanet(480,120,45);
addEventListener(Event.ENTER_FRAME,update);
stage.addEventListener(MouseEvent.CLICK,createDebris);
}
private function createDebris(e:MouseEvent):void {
addBox(mouseX,mouseY,20,20);
}
private function addPlanet(pX:Number,pY:Number,r:Number):void {
var fixtureDef:b2FixtureDef = new b2FixtureDef();
fixtureDef.restitution=0;
fixtureDef.density=1;
var circleShape:b2CircleShape=new b2CircleShape(r/worldScale);
fixtureDef.shape=circleShape;
var bodyDef:b2BodyDef=new b2BodyDef();
bodyDef.userData=new Sprite();
bodyDef.position.Set(pX/worldScale,pY/worldScale);
var thePlanet:b2Body=world.CreateBody(bodyDef);
planetVector.push(thePlanet);
thePlanet.CreateFixture(fixtureDef);
orbitCanvas.graphics.drawCircle(pX,pY,r*3);
}
private function addBox(pX:Number,pY:Number,w:Number,h:Number):void {
var polygonShape:b2PolygonShape = new b2PolygonShape();
polygonShape.SetAsBox(w/worldScale/2,h/worldScale/2);
var fixtureDef:b2FixtureDef = new b2FixtureDef();
fixtureDef.density=1;
fixtureDef.friction=1;
fixtureDef.restitution=0;
fixtureDef.shape=polygonShape;
var bodyDef:b2BodyDef = new b2BodyDef();
bodyDef.type=b2Body.b2_dynamicBody;
bodyDef.position.Set(pX/worldScale,pY/worldScale);
var box:b2Body=world.CreateBody(bodyDef);
debrisVector.push(box);
box.CreateFixture(fixtureDef);
}
private function debugDraw():void {
var debugDraw:b2DebugDraw = new b2DebugDraw();
var debugSprite:Sprite = new Sprite();
addChild(debugSprite);
debugDraw.SetSprite(debugSprite);
debugDraw.SetDrawScale(worldScale);
debugDraw.SetFlags(b2DebugDraw.e_shapeBit|b2DebugDraw.e_jointBit);
debugDraw.SetFillAlpha(0.5);
world.SetDebugDraw(debugDraw);
}
private function update(e:Event):void {
world.Step(1/30, 10, 10);
world.ClearForces();
for (var i:int=0; i<debrisVector.length; i++) {
var debrisPosition:b2Vec2=debrisVector.GetWorldCenter();
for (var j:int=0; j<planetVector.length; j++) {
var planetShape:b2CircleShape=planetVector.GetFixtureList().GetShape() as b2CircleShape;
var planetRadius:Number=planetShape.GetRadius();
var planetPosition:b2Vec2=planetVector.GetWorldCenter();
var planetDistance:b2Vec2=new b2Vec2(0,0);
planetDistance.Add(debrisPosition);
planetDistance.Subtract(planetPosition);
var finalDistance:Number=planetDistance.Length();
if (finalDistance<=planetRadius*3) {
planetDistance.NegativeSelf();
var vecSum:Number=Math.abs(planetDistance.x)+Math.abs(planetDistance.y);
planetDistance.Multiply((1/vecSum)*planetRadius/finalDistance);
debrisVector.ApplyForce(planetDistance,debrisVector.GetWorldCenter());
}
}
}
world.DrawDebugData();
}
}
}整个代码只创造静态主体(星球),让你通过点击鼠标放置动态主体(碎屑)。脚本的唯一有趣之处在于更新函数的循环语句,下面我将逐行进行解释。for (var i:int=0; i<debrisVector.length; i++) {检测之前存储于矢量Vector中的所有碎屑的循环语句在第14行代码中呈现,在第54行进行更新。var debrisPosition:b2Vec2=debrisVector.GetWorldCenter();设定碎屑位置。for (var j:int=0; j<planetVector.length; j++) {检测之前存储于矢量Vector中的所有星球的循环语句将在第13行代码中呈现,在第38行进行更新。var planetShape:b2CircleShape=planetVector.GetFixtureList().GetShape() as b2CircleShape;我需要知道星球的质量,因为质量越大,重力吸引力就越强烈。遗憾的是,Box2D静态主体没有质量,所以我需要得到星球的圆形模型……var planetRadius:Number=planetShape.GetRadius();……设定它的半径。所以在这种情况下,半径越大,重力吸引力就越强烈。var planetPosition:b2Vec2=planetVector.GetWorldCenter();设定星球的位置。var planetDistance:b2Vec2=new b2Vec2(0,0);创建新的b2Vec2变量,它将存储星球和碎屑之间的距离。planetDistance.Add(debrisPosition);添加碎屑坐标轴,然后……planetDistance.Subtract(planetPosition);……扣除星球坐标轴。var finalDistance:Number=planetDistance.Length();计算星球和碎屑之间的距离。if (finalDistance<=planetRadius*3) {检查碎屑是否受到星球重力的影响(游戏邦注:这里碎屑的半径不能大于星球半径的3倍)。planetDistance.NegativeSelf();插入星球距离,这样force会按照星球起点的方向移动碎屑。var vecSum:Number=Math.abs(planetDistance.x)+Math.abs(planetDistance.y);合计距离矢量分量。因此碎屑远离星球时,重力吸引力应减弱;碎屑靠近星球时,重力吸引力应增强。planetDistance.Multiply((1/vecSum)*planetRadius/finalDistance);这是随着我们逐步远离星球,削弱重力的最后公式。debrisVector.ApplyForce(planetDistance,debrisVector.GetWorldCenter());最终force将被运用至碎屑中。下面是最终结果:

花の妖精 发表于 2012-5-3 09:39:57

看不懂~~~哈哈!

風中粒沙 发表于 2012-5-3 10:41:51

顶下楼主!!!

glh4451 发表于 2012-5-14 19:28:31

朋友不错,谢谢您的努力,顶了

plstn 发表于 2012-5-22 17:55:35

谢谢楼主,楼主太好人了

glh4451 发表于 2012-5-28 20:52:52

哈哈,有意思。

海螺融兵 发表于 2012-7-6 17:06:18

路过!!!!!!
页: [1]
查看完整版本: 论述如何基于Box2D模拟星球重力效果