http://www.ladeng6666.com/blog/2012/09/06/box2d%E8%87%AA%E5%AE%9A%E4%B9%89%E9%87%8D%E5%8A%9B/
Box2D自定义重力
今天我们一起来学习自定义刚体重力(修改重力方向或消除重力)。
在Box2D中创建的非静态刚体,默认情况下都会受到重力作用,自然下落。这是我们希望的。不过有些特殊情况,重力反而是我们不想要的,比如在太空环境中,所有的物体都是处于失重状态,这时候就不需要重力了。这种情况应该怎么用Box2D实现呢?
在此要特别感谢Emanuele的Managing multiple gravities with Box2D,我也是从中学习到了接下来要讲的方法。
实现方法有两种:一、加减法抵消重力;二、重置重力。下面我们来详细一下这两种方法。
一、加减法抵消重力
重力也是里的以一种,也有大小和方向。它的方向与y轴一致,所以可以用一个正数表示重力,如:G。那么要消除重力,即受力为0,只要G-G=0就可以了。也就是说,再给刚体施加一个-G的力,负数表示力的方向与y轴相反。
关于对刚体施加里的方法,请参考让刚体听到的——ApplyForce一文。
值得注意的是,重力等于刚体的质量乘以重力加速度,所以在施加外力时,不要忘了乘以刚体的质量b2Body.GetMass()。在下面的效果中,点击鼠标创建一个不受重力影响的刚体:
完整的代码即注释如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
package
{
import
Box2D
.
Common
.
Math
.
b2Vec2
;
import
Box2D
.
Dynamics
.
b2Body
;
import
Box2D
.
Dynamics
.
b2World
;
import
flash
.
display
.
Sprite
;
import
flash
.
events
.
Event
;
import
flash
.
events
.
MouseEvent
;
/**
* http://www.ladeng6666.com
* @author ladeng6666
*/
public
class
ApplyForce
extends
Sprite
{
private
var
world
:
b2World
;
private
var
debugSprite
:
Sprite
;
private
var
body
:
b2Body
;
public
function
ApplyForce
(
)
{
//创建基本的世界
world
=
LDEasyBox2D
.
createWorld
(
)
;
debugSprite
=
LDEasyBox2D
.
createDebug
(
world
)
;
addChild
(
debugSprite
)
;
//创建地面,
LDEasyBox2D
.
createWrapWall
(
world
,
stage
)
;
//侦听事件
addEventListener
(
Event
.
ENTER_FRAME
,
loop
)
;
stage
.
addEventListener
(
MouseEvent
.
MOUSE_DOWN
,
onStageMouseDown
)
;
}
private
function
onStageMouseDown
(
e
:
MouseEvent
)
:
void
{
//鼠标按下时,在鼠标位置创建一个刚体
body
=
LDEasyBox2D
.
createCircle
(
world
,
mouseX
,
mouseY
,
Math
.
random
(
)
*
30
+
10
,
false
)
;
}
private
function
loop
(
e
:
Event
)
:
void
{
//遍历所有的刚体,并对它们施加一个与重力相等的向上的外力,平衡重力,模拟失重效果
for
(
var
body
:
b2Body
=
world
.
GetBodyList
(
)
;
body
;
body
=
body
.
GetNext
(
)
)
{
body
.
ApplyForce
(
new
b2Vec2
(
0
,
-
10
*
body
.
GetMass
(
)
)
,
body
.
GetWorldCenter
(
)
)
;
}
//更新Box2D世界
LDEasyBox2D
.
updateWorld
(
world
)
;
}
}
}
|
效果看起来不错哦。不知道你注意到没有,所有的刚体都是醒着的(Box2D中对于停止运动的刚体,会自动将其设置为睡眠sleep状态,并显示灰色,并且不对其进行物理模拟,以节省CPU开支)。这样CPU的负担并没有降低。
二、重置重力
Box2D中的Dynamics包下有一个b2Island类,类中有一个Solve方法,这方法通过gravity形参对刚体进行重力模拟,代码如下:
1
2
3
|
b
.
m_linearVelocity
.
x
+
=
step
.
dt *
(
gravity
.
x
+
b
.
m_invMass *
b
.
m_force
.
x
)
;
b
.
m_linearVelocity
.
y
+
=
step
.
dt *
(
gravity
.
y
+
b
.
m_invMass *
b
.
m_force
.
y
)
;
b
.
m_angularVelocity
+
=
step
.
dt *
b
.
m_invI *
b
.
m_torque
;
|
所谓重置重力,就是在这里动手脚了。要怎么做呢?把这个gravity形参改成我们想要的重力,比如可以像下面的代码,设置为0.
1
2
3
4
|
gravity
=
new
b2Vec2
(
0
,
0
)
;
b
.
m_linearVelocity
.
x
+
=
step
.
dt *
(
gravity
.
x
+
b
.
m_invMass *
b
.
m_force
.
x
)
;
b
.
m_linearVelocity
.
y
+
=
step
.
dt *
(
gravity
.
y
+
b
.
m_invMass *
b
.
m_force
.
y
)
;
b
.
m_angularVelocity
+
=
step
.
dt *
b
.
m_invI *
b
.
m_torque
;
|
或者我们用了一个更便于重用的方法,在b2Body类添加一个b2Vec2类型的公共属性m_customGravity,然后在b2Island.Solve()方法中,判断是否设置了刚体的m_customGravity属性,如果有,则用m_customGravity进行重力模拟计算。然后我们在创建刚体时,只要设置一下m_customGravity属性就可以轻松重置重力了。
1
2
3
4
5
6
7
8
9
10
11
12
|
//2012-7-27
//added by ladeng6666
//如果刚体有自定义m_customGravity属性,则用其进行重力模拟计算
if
(
b
.
m_customGravity
!=
null
)
{
LDgravity
=
b
.
m_customGravity
;
}
else
{
LDgravity
=
gravity
;
}
//用更新后的LDgravity属性进行重力模拟计算
b
.
m_linearVelocity
.
x
+
=
step
.
dt *
(
LDgravity
.
x
+
b
.
m_invMass *
b
.
m_force
.
x
)
;
b
.
m_linearVelocity
.
y
+
=
step
.
dt *
(
LDgravity
.
y
+
b
.
m_invMass *
b
.
m_force
.
y
)
;
b
.
m_angularVelocity
+
=
step
.
dt *
b
.
m_invI *
b
.
m_torque
;
|
在下面的效果中,点击鼠标创建一个不受重力影响的刚体(当然你可以随意设置customer_gravity属性,让重力向上、向左、向右)。按下键盘空格键,创建一个向上"掉"的矩形刚体。点击刚体,可以进行拖动。
完整的代码和注释如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
|
package
{
import
Box2D
.
Common
.
Math
.
b2Vec2
;
import
Box2D
.
Dynamics
.
b2Body
;
import
Box2D
.
Dynamics
.
b2World
;
import
flash
.
display
.
Sprite
;
import
flash
.
events
.
Event
;
import
flash
.
events
.
KeyboardEvent
;
import
flash
.
events
.
MouseEvent
;
import
flash
.
ui
.
Keyboard
;
/**
* http://www.ladeng6666.com
* @author ladeng6666
*/
public
class
CustomerGravity
extends
Sprite
{
private
var
world
:
b2World
;
private
var
debugSprite
:
Sprite
;
private
var
body
:
b2Body
;
public
function
CustomerGravity
(
)
{
//创建基本的Box2D世界
world
=
LDEasyBox2D
.
createWorld
(
)
;
debugSprite
=
LDEasyBox2D
.
createDebug
(
world
)
;
addChild
(
debugSprite
)
;
LDEasyBox2D
.
stage
=
stage
;
//创建地面,
LDEasyBox2D
.
createWrapWall
(
world
,
stage
)
;
//侦听事件
addEventListener
(
Event
.
ENTER_FRAME
,
loop
)
;
stage
.
addEventListener
(
MouseEvent
.
MOUSE_DOWN
,
mouseEventHandler
)
;
stage
.
addEventListener
(
MouseEvent
.
MOUSE_UP
,
mouseEventHandler
)
;
stage
.
addEventListener
(
KeyboardEvent
.
KEY_DOWN
,
onStageKeyDown
)
;
}
private
function
onStageKeyDown
(
e
:
KeyboardEvent
)
:
void
{
//当按下键盘空格键时,创建一个重力向上的矩形刚体
if
(
e
.
keyCode
==
Keyboard
.
SPACE
)
{
body
=
LDEasyBox2D
.
createBox
(
world
,
Math
.
random
(
)
*
400
+
50
,
400
,
Math
.
random
(
)
*
30
+
10
,
Math
.
random
(
)
*
30
+
10
)
;
body
.
m_customGravity
=
new
b2Vec2
(
0
,
-
10
)
;
}
}
private
function
mouseEventHandler
(
e
:
MouseEvent
)
:
void
{
//鼠标按下时
if
(
e
.
type
==
MouseEvent
.
MOUSE_DOWN
)
{
//确认是否点击到刚体
var
dragBody
:
b2Body
=
LDEasyBox2D
.
getBodyAtMouse
(
world
)
;
if
(
dragBody
!=
null
)
{
//如果点击到了,则拖动刚体
LDEasyBox2D
.
startDragBody
(
world
,
dragBody
)
;
}
else
{
//否则创建一个圆形的,不受重力影响的刚体
body
=
LDEasyBox2D
.
createCircle
(
world
,
mouseX
,
mouseY
,
Math
.
random
(
)
*
30
+
10
,
false
)
;
body
.
m_customGravity
=
new
b2Vec2
(
0
,
0
)
;
}
//当鼠标弹起时
}
else
if
(
e
.
type
==
MouseEvent
.
MOUSE_UP
)
{
//停止拖动刚体
LDEasyBox2D
.
stopDragBody
(
world
)
;
}
}
private
function
loop
(
e
:
Event
)
:
void
{
//更新Box2D世界
LDEasyBox2D
.
updateWorld
(
world
)
;
}
}
}
|
代码重点是在45,61行,看到没有customer_gravity按照我们希望的样子自定义了重力,是不是很好用?
自定义重力可以实现的效果很多,如失重、上升的气球等等,发挥你的想象力,没有什么做不到的。Fighting!
代码中用到了我写的静态类LDEasyBox2D,可以有效的简化代码,具体请参考这里。