论述如何基于Box2D模拟星球重力效果
随着《Angry Birds Space》的问世,我想你定非常疑惑要如何通过Box2D模拟星球重力。
基本原理非常简单。
首先,太空没有重力,所以你将通过如下方式创建没有重力的b2World世界:
private var world:b2World=new b2World(new b2Vec2(0,0),true);
abspace
接着就是根据主体和球体位置落实Force。
参考如下脚本:
package {
import rite;
import ent;
import useEvent;
import namics.*;
import llision.*;
import aPEs.*;
import th.*;
import ints.*;
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.=new Vector.();
private var debrisVector:Vector.=new Vector.();
private var orbitCanvas:Sprite=new Sprite();
public function Main() {
addChild(orbitCanvas);
neStyle(1,0xff0000);
debugDraw();
addPlanet(180,240,90);
addPlanet(480,120,45);
addEventListener(TER_FRAME,update);
dEventListener(ICK,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();
stitution=0;
nsity=1;
var circleShape:b2CircleShape=new b2CircleShape(r/worldScale);
ape=circleShape;
var bodyDef:b2BodyDef=new b2BodyDef();
erData=new Sprite();
t(pX/worldScale,pY/worldScale);
var thePlanet:b2Body=eateBody(bodyDef);
sh(thePlanet);
eateFixture(fixtureDef);
awCircle(pX,pY,r*3);
}
private function addBox(pX:Number,pY:Number,w:Number,h:Number):void {
var polygonShape:b2PolygonShape = new b2PolygonShape();
tAsBox(w/worldScale/2,h/worldScale/2);
var fixtureDef:b2FixtureDef = new b2FixtureDef();
nsity=1;
iction=1;
stitution=0;
ape=polygonShape;
var bodyDef:b2BodyDef = new b2BodyDef();
pe=b2Body.b2_dynamicBody;
t(pX/worldScale,pY/worldScale);
var box:b2Body=eateBody(bodyDef);
sh(box);
eateFixture(fixtureDef);
}
private function debugDraw():void {
var debugDraw:b2DebugDraw = new b2DebugDraw();
var debugSprite:Sprite = new Sprite();
addChild(debugSprite);
tSprite(debugSprite);
tDrawScale(worldScale);
tFlags(b2DebugDraw.e_shapeBit|b2DebugDraw.e_jointBit);
tFillAlpha(0.5);
tDebugDraw(debugDraw);
}
private function update(e:Event):void {
ep(1/30, 10, 10);
earForces();
for (var i:int=0; i
var debrisPosition:b2Vec2=debrisVector[i].GetWorldCenter();
for (var j:int=0; j
var planetShape:b2CircleShape=planetVector[j].GetFixtureList().GetShape() as b2CircleShape;
var planetRadius:Number=tRadius();
var planetPosition:b2Vec2=planetVector[j].GetWorldCenter();
var planetDistance:b2Vec2=new b2Vec2(0,0);
d(debrisPosition);
btract(planetPosition);
var finalDistance:Number=ngth();
if (finalDistance<=planetRadius*3) {
gativeSelf();
var vecSum:Number=s(planetDistance.x)+s(planetDistance.y);
ltiply((1/vecSum)*planetRadius/finalDistance);
debrisVector[i].APPlyForce(planetDistance,debrisVector[i].GetWorldCenter());
}
}
}
awDebugData();
}
}
}
整个代码只创造静态主体(星球),让你通过点击鼠标放置动态主体(碎屑)。
脚本的唯一有趣之处在于更新函数的循环语句,下面我将逐行进行解释。
for (var i:int=0; i
检测之前存储于矢量Vector中的所有碎屑的循环语句在第14行代码中呈现,在第54行进行更新。
var debrisPosition:b2Vec2=debrisVector[i].GetWorldCenter();
设定碎屑位置。
for (var j:int=0; j
检测之前存储于矢量Vector中的所有星球的循环语句将在第13行代码中呈现,在第38行进行更新。
var planetShape:b2CircleShape=planetVector[j].GetFixtureList().GetShape() as b2CircleShape;
我需要知道星球的质量,因为质量越大,重力吸引力就越强烈。遗憾的是,Box2D静态主体没有质量,所以我需要得到星球的圆形模型……
var planetRadius:Number=tRadius();
……设定它的半径。所以在这种情况下,半径越大,重力吸引力就越强烈。
var planetPosition:b2Vec2=planetVector[j].GetWorldCenter();
设定星球的位置。
var planetDistance:b2Vec2=new b2Vec2(0,0);
创建新的b2Vec2变量,它将存储星球和碎屑之间的距离。
d(debrisPosition);
添加碎屑坐标轴,然后……
btract(planetPosition);
……扣除星球坐标轴。
var finalDistance:Number=ngth();
计算星球和碎屑之间的距离。
if (finalDistance<=planetRadius*3) {
检查碎屑是否受到星球重力的影响(游戏邦注:这里碎屑的半径不能大于星球半径的3倍)。
gativeSelf();
插入星球距离,这样force会按照星球起点的方向移动碎屑。
var vecSum:Number=s(planetDistance.x)+s(planetDistance.y);
合计距离矢量分量。因此碎屑远离星球时,重力吸引力应减弱;碎屑靠近星球时,重力吸引力应增强。
ltiply((1/vecSum)*planetRadius/finalDistance);
这是随着我们逐步远离星球,削弱重力的最后公式。
debrisVector[i].ApplyForce(planetDistance,debrisVector[i].GetWorldCenter());
最终force将被运用至碎屑中。
下面是最终结果:
debris