游戏邦在:
杂志专栏:
gamerboom.com订阅到鲜果订阅到抓虾google reader订阅到有道订阅到QQ邮箱订阅到帮看

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

发布时间:2012-04-18 16:52:32 Tags:,,

作者:Emanuele Feronato

随着《Angry Birds Space》的问世,我想你定非常疑惑要如何通过Box2D模拟星球重力。

基本原理非常简单。

首先,太空没有重力,所以你将通过如下方式创建没有重力的b2World世界:

private var world:b2World=new b2World(new b2Vec2(0,0),true);

abspace from emanueleferonato.com

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[i].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[i].ApplyForce(planetDistance,debrisVector[i].GetWorldCenter());
}
}
}
world.DrawDebugData();
}
}
}

整个代码只创造静态主体(星球),让你通过点击鼠标放置动态主体(碎屑)。

脚本的唯一有趣之处在于更新函数的循环语句,下面我将逐行进行解释。

for (var i:int=0; i<debrisVector.length; i++) {

检测之前存储于矢量Vector中的所有碎屑的循环语句在第14行代码中呈现,在第54行进行更新。

var debrisPosition:b2Vec2=debrisVector[i].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[i].ApplyForce(planetDistance,debrisVector[i].GetWorldCenter());

最终force将被运用至碎屑中。

下面是最终结果:

debris from emanueleferonato.com

debris from emanueleferonato.com

本文为游戏邦/gamerboom.com编译,拒绝任何不保留版权的转载,如需转载请联系:游戏邦

Simulate radial gravity (also known as “planet gravity”) with Box2D as seen on Angry Birds Space

by Emanuele Feronato

With the launch of Angry Birds Space I am sure you are wondering how to simulate planet gravity with Box2D.

And guess what… the basics are very easy.

First, in space there’s no gravity, so you will create a b2World world without gravity this way:

Then, it’s just a matter of applying Forces according to bodies and planets position.

Look at this script:

The whole code just create static bodies (planets) and let you place dynamic bodies (debris) with the click of the mouse.

The only interesting part of the script is the for loop in the update function, which I’ll explain line by line:

Loop which scans for all debris previously stored in debrisVector Vector declared at line 14 and updated at line 54

Gets debris position

Loop which scans for all planets previously stored in planetVector Vector declared at line 13 and updated at line 38

I need to know the mass of the planet because the bigger the mass, the more intense the gravity attraction. Unfortunately Box2D static bodies do not have mass, so I need to get the circle shape of the planet…

… and get its radius. So in this case the bigger the radius, the more intense the gravity attraction

Gets planet position

Creates a new b2Vec2 variable which will store the distance between the planet and the debris

Adds debris coordinates, then…

… subtracts planet coordinates

Calculates the distance between the planet and the debris

Checks if the debris should be affected by planet gravity (in this case, the debris must be within a radius of three times the planet radius)

Inverts planet distance, so that the force will move the debris in the direction of the planet origin

Gets the sum of distance vector components. I will need this to make gravity attraction weaker when the debris is far from the planet, and stronger when the debris is getting close to the planet

This is the final formula to make the gravity weaker as we move far from the planet

And finally the force can be applied to debris

This is the result:(Source:emanueleferonato


上一篇:

下一篇: