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

2012-05-03 09:38 发布

5557 6 0

随着《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[j].GetFixtureList().GetShape() as b2CircleShape;
var planetRadius:Number=planetShape.GetRadius();
var planetPosition:b2Vec2=planetVector[j].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[j].GetFixtureList().GetShape() as b2CircleShape;

我需要知道星球的质量,因为质量越大,重力吸引力就越强烈。遗憾的是,Box2D静态主体没有质量,所以我需要得到星球的圆形模型……

var planetRadius:Number=planetShape.GetRadius();

……设定它的半径。所以在这种情况下,半径越大,重力吸引力就越强烈。

var planetPosition:b2Vec2=planetVector[j].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将被运用至碎屑中。

下面是最终结果:


B Color Smilies

全部评论6

你可能喜欢

论述如何基于Box2D模拟星球重力效果 
联系
我们
快速回复 返回顶部 返回列表