box2d 碰撞检测_谁动了我的刚体——Box2D碰撞检测

本文介绍了如何在Box2D中利用b2Contact和b2ContactListener来获取碰撞对象,从而实现精确的碰撞检测。通过world.GetContactList()方法或自定义b2ContactListener,可以监听并处理碰撞事件,例如为碰撞对象添加冲力。文中还提供了一个实际示例,展示了如何判断和处理不同类型的碰撞对象。
摘要由CSDN通过智能技术生成

今天我们来学习用Box2D的b2Contact和b2ContactListener来获取碰撞对象(Box2D中用contact一词来表示碰撞,知道这一点,对后面的理解会有帮助)。

Box2D是一个非常强大的2D物理引擎,可以帮我们实现精确的碰撞检测,并模拟2D碰撞。但有时候只知道”碰撞了”是不够了,就像我在”谈谈碰撞检测“里讲到的,针对不同的游戏,碰撞后的处理方式是不同的,可能是变更运动轨迹(如桌球游戏)、可能是销毁对象(如愤怒的小鸟)等等。而实现这一切可能的效果,首先要先知道碰撞的对象,所以就有了今天的教程。

Box2D中获取碰撞对象的方法有两种。一个是通过world.GetContactList().bodyA和bodyB来获取碰撞双方;另外一个是自定义Box2D.Dynamics下的b2ContactListener类,侦听碰撞后的事件,然后做进一步的处理。下面我们来看看具体的实现方法。

一、用world.GetContactList()方法获取碰撞对象

world.GetContactList()会返回一个b2Contact对象。前面我说过的,Box2D中contact是碰撞的意思,所以可以猜想到,b2Contact肯定跟碰撞有关。是的,b2Contact用来管理碰撞的shape,任何有超过两个及以上接触点的刚体,Box2D都认为发生了碰撞,并用b2Contact来管理。

通过b2Contact的GetFixtureA()和GetFixtureB()方法,我们可以获取碰撞对象的b2Fixture属性引用,进而获取碰撞对象。具体代码举例如下:

var contactList:b2Contact = world.GetContactList();

var bodyA:b2Body = contactList.GetFixtureA().GetBody();

var bodyB:b2Body = contactList.GetFixtureB().GetBody();

1

2

3

varcontactList:b2Contact=world.GetContactList();

varbodyA:b2Body=contactList.GetFixtureA().GetBody();

varbodyB:b2Body=contactList.GetFixtureB().GetBody();

二、用b2ContactListener获取碰撞对象

从b2ContactListener的名字上大家应该可以猜到,它是Box2D对象碰撞检测侦听器,当刚体之间发生碰撞时,Box2D引擎会自动调用这个类的相关方法,然后做进一步的处理。这些方法包括:

BeginContact:当碰撞发生时触发该方法

EndContact:当碰撞结束时触发该方法

使用b2ContactListener的方法有两种。一、修改b2ContactListener的BeginContact和EndContact方法,添加碰撞处理代码;二、继承b2ContactListener类,如自定义一个myContactListener子类,然后重写子类的BeginContact和EndContact方法,然后将子类传递给world.world.SetContactListener()方法,自定义碰撞侦听器。

package

{

import Box2D.Common.Math.b2Vec2;

import Box2D.Dynamics.b2ContactListener;

import Box2D.Dynamics.Contacts.b2Contact;

/**

* ...

* @author ladeng6666

*/

public class MyContactListener extends b2ContactListener

{

public function MyContactListener()

{

super();

}

override public function BeginContact(contact:b2Contact):void

{

//碰撞后给碰撞对象添加向上的冲力

contact.GetFixtureA().GetBody().ApplyImpulse(new b2Vec2(0, -10));

contact.GetFixtureB().GetBody().ApplyImpulse(new b2Vec2(0, -10));

}

override public function EndContact(contact:b2Contact):void

{

//同样可以和BeginContact方法一样,定义一些碰撞处理方式

}

}

}

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

package

{

importBox2D.Common.Math.b2Vec2;

importBox2D.Dynamics.b2ContactListener;

importBox2D.Dynamics.Contacts.b2Contact;

/**

* ...

* @author ladeng6666

*/

publicclassMyContactListenerextendsb2ContactListener

{

publicfunctionMyContactListener()

{

super();

}

overridepublicfunctionBeginContact(contact:b2Contact):void

{

//碰撞后给碰撞对象添加向上的冲力

contact.GetFixtureA().GetBody().ApplyImpulse(newb2Vec2(0,-10));

contact.GetFixtureB().GetBody().ApplyImpulse(newb2Vec2(0,-10));

}

overridepublicfunctionEndContact(contact:b2Contact):void

{

//同样可以和BeginContact方法一样,定义一些碰撞处理方式

}

}

}

三、如何使用Box2D的碰撞对象

在Flash中编写碰撞检测代码时,通常我们会用A对象(如游戏主角)的hitTest或hitTestObject方法检查它与另外一个对象的碰撞,当碰撞发生时,分别对A和B进行碰撞处理。这里我们很清楚哪个是A对象(游戏主角),那个是B对象(敌人)。

但是在b2Contact或b2ContactListener中,我们获取的bodyA和bodyB无法知道哪个是游戏主角,哪个是敌人,分不出个青红皂白,所以只能一棒子打死啦。我的意思是,比如我现在想找到碰撞的对象是否是游戏主角,那么就得分别确认一下bodyA和bodyB了,游戏碰撞对象种类越多,判断越复杂。具体在下面的实例中,我会在代码注释里解释。

在下面的示例中,有一个静态的矩形对象,屏幕顶部会不断的掉下圆形和矩形刚体,当发生碰撞后,圆形刚体会向左运动,矩形会向右运动。

完整的代码和注释如下:

package

{

import Box2D.Dynamics.Contacts.b2Contact;

import flash.display.MovieClip;

import flash.events.MouseEvent;

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.ui.Keyboard;

/**

* http://www.ladeng6666.com

* @author ladeng6666

*/

public class Main extends Sprite

{

//创建世界的基本元素

private var world:b2World;

private var debugSprite:Sprite;

private var contactList:b2Contact;

private var age:Number = 30;

private var interval:Number = 0;

public function Main()

{

//创建一个world世界

LDEasyBox2D.stage = this;

world=LDEasyBox2D.createWorld();

debugSprite=LDEasyBox2D.createDebug(world);

addChild(debugSprite);

//创建刚体数据,通过个对象的.name属性,可以判断对象的类型,是矩形?是圆形?还是静态地面

var bd:BodyData = new BodyData();

bd.name = "ground";

//创建矩形刚体

LDEasyBox2D.createBox(world, stage.stageWidth / 2, 300, 400,20, true,bd);

//侦听事件

addEventListener(Event.ENTER_FRAME, loop);

}

private function createBodies():void {

//没30帧随机创建一个刚体,可能是圆形刚体,也可能是矩形刚体

if (++interval > age) {

var bd:BodyData = new BodyData();

if (Math.random() > 0.5) {

//通过BodyData对象定义刚体的类型,在碰撞检测时,我们会通过这个属性判断刚体的类型

bd.name = "rect";

LDEasyBox2D.createBox(world, Math.random() * 300 + 100, 0, 40, 30, false, bd);

}else {

//通过BodyData对象定义刚体的类型,在碰撞检测时,我们会通过这个属性判断刚体的类型

bd.name = "circle";

LDEasyBox2D.createCircle(world, Math.random() * 300 + 100, 0, 20, false, bd);

}

interval = 0;

}

}

private function loop(e:Event):void

{

//更新世界

LDEasyBox2D.updateWorld(world);

//创建刚体

createBodies();

//获取world的b2Contact对象

contactList = world.GetContactList();

if (contactList != null) {

//如果发生了碰撞,记录碰撞的双方bodyA和bodyB

var bodyA:b2Body = contactList.GetFixtureA().GetBody();

var bodyB:b2Body = contactList.GetFixtureB().GetBody();

/**

* 碰撞的双方有3种可能:

* 1.动态刚体与静态地面碰撞

* 2.动态刚体与动态刚体碰撞

*

* 我们需要处理的动态刚体与静态地面碰撞,并移动动态刚体,但是我们不知道哪个是静态地面

* 所以只能分别对bodyA和bodyB进行判断

*/

//判断bodyA是不是静态地面

if (bodyA.GetUserData().name == "ground") {

//如果是静态地面,然后根据bodyB的类型,进行不同的处理

if (bodyB.GetUserData().name == "rect") {

bodyB.ApplyImpulse(new b2Vec2(1*bodyB.GetMass()), bodyB.GetWorldCenter());

}else {

bodyB.ApplyImpulse(new b2Vec2(-1*bodyB.GetMass()), bodyB.GetWorldCenter());

}

//判断bodyA是不是静态地面

//试着删除elseif里面的内容,看看不同的结果(矩形刚体将不会向右移动)

}else if (bodyB.GetUserData().name == "ground") {

//判断bodyA是不是静态地面

if (bodyA.GetUserData().name == "rect") {

bodyA.ApplyImpulse(new b2Vec2(1*bodyA.GetMass()), bodyA.GetWorldCenter());

}else {

bodyA.ApplyImpulse(new b2Vec2(-1*bodyA.GetMass()), bodyA.GetWorldCenter());

}

}

}

//清除超出屏幕的刚体

var body:b2Body = world.GetBodyList();

for (; body; body = body.GetNext()) {

if (body.GetPosition().y > 400/30) {

world.DestroyBody(body);

}

}

}

}

}

//自定义一个用户数据类

import flash.display.Sprite;

class BodyData extends flash.display.Sprite{

}

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

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

package

{

importBox2D.Dynamics.Contacts.b2Contact;

importflash.display.MovieClip;

importflash.events.MouseEvent;

importBox2D.Common.Math.b2Vec2;

importBox2D.Dynamics.b2Body;

importBox2D.Dynamics.b2World;

importflash.display.Sprite;

importflash.events.Event;

importflash.events.KeyboardEvent;

importflash.ui.Keyboard;

/**

* http://www.ladeng6666.com

* @author ladeng6666

*/

publicclassMainextendsSprite

{

//创建世界的基本元素

privatevarworld:b2World;

privatevardebugSprite:Sprite;

privatevarcontactList:b2Contact;

privatevarage:Number=30;

privatevarinterval:Number=0;

publicfunctionMain()

{

//创建一个world世界

LDEasyBox2D.stage=this;

world=LDEasyBox2D.createWorld();

debugSprite=LDEasyBox2D.createDebug(world);

addChild(debugSprite);

//创建刚体数据,通过个对象的.name属性,可以判断对象的类型,是矩形?是圆形?还是静态地面

varbd:BodyData=newBodyData();

bd.name="ground";

//创建矩形刚体

LDEasyBox2D.createBox(world,stage.stageWidth/2,300,400,20,true,bd);

//侦听事件

addEventListener(Event.ENTER_FRAME,loop);

}

privatefunctioncreateBodies():void{

//没30帧随机创建一个刚体,可能是圆形刚体,也可能是矩形刚体

if(++interval>age){

varbd:BodyData=newBodyData();

if(Math.random()>0.5){

//通过BodyData对象定义刚体的类型,在碰撞检测时,我们会通过这个属性判断刚体的类型

bd.name="rect";

LDEasyBox2D.createBox(world,Math.random()*300+100,0,40,30,false,bd);

}else{

//通过BodyData对象定义刚体的类型,在碰撞检测时,我们会通过这个属性判断刚体的类型

bd.name="circle";

LDEasyBox2D.createCircle(world,Math.random()*300+100,0,20,false,bd);

}

interval=0;

}

}

privatefunctionloop(e:Event):void

{

//更新世界

LDEasyBox2D.updateWorld(world);

//创建刚体

createBodies();

//获取world的b2Contact对象

contactList=world.GetContactList();

if(contactList!=null){

//如果发生了碰撞,记录碰撞的双方bodyA和bodyB

varbodyA:b2Body=contactList.GetFixtureA().GetBody();

varbodyB:b2Body=contactList.GetFixtureB().GetBody();

/**

* 碰撞的双方有3种可能:

* 1.动态刚体与静态地面碰撞

* 2.动态刚体与动态刚体碰撞

*

* 我们需要处理的动态刚体与静态地面碰撞,并移动动态刚体,但是我们不知道哪个是静态地面

* 所以只能分别对bodyA和bodyB进行判断

*/

//判断bodyA是不是静态地面

if(bodyA.GetUserData().name=="ground"){

//如果是静态地面,然后根据bodyB的类型,进行不同的处理

if(bodyB.GetUserData().name=="rect"){

bodyB.ApplyImpulse(newb2Vec2(1*bodyB.GetMass()),bodyB.GetWorldCenter());

}else{

bodyB.ApplyImpulse(newb2Vec2(-1*bodyB.GetMass()),bodyB.GetWorldCenter());

}

//判断bodyA是不是静态地面

//试着删除elseif里面的内容,看看不同的结果(矩形刚体将不会向右移动)

}elseif(bodyB.GetUserData().name=="ground"){

//判断bodyA是不是静态地面

if(bodyA.GetUserData().name=="rect"){

bodyA.ApplyImpulse(newb2Vec2(1*bodyA.GetMass()),bodyA.GetWorldCenter());

}else{

bodyA.ApplyImpulse(newb2Vec2(-1*bodyA.GetMass()),bodyA.GetWorldCenter());

}

}

}

//清除超出屏幕的刚体

varbody:b2Body=world.GetBodyList();

for(;body;body=body.GetNext()){

if(body.GetPosition().y>400/30){

world.DestroyBody(body);

}

}

}

}

}

//自定义一个用户数据类

importflash.display.Sprite;

classBodyDataextendsflash.display.Sprite{

}

代码中用到了我写的静态类LDEasyBox2D,可以有效的简化代码,具体请参考这里或者googleCode

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值