用PhysicsEditor快速创建自定义Box2D刚体
转自:http://www.ladeng6666.com/blog/2012/12/09/create-customer-body-shape-using-physicseditor-step1/
PhysicsEditor,相信很多人都已经很熟悉了,通过它的可视化编辑界面,点一点,拉一拉就可以轻松创建任意的多边形刚体模型,并将这个模型数据导出成我们所需的格式,例如AS3类。PhysicsEditor支持Box2D、Cocos2d、Nape等多种2D物理引擎。闲话少说,我们马上开始学习PhysicsEditor。
PS:如果你喜欢学习英文资料,可以参考这里。
在学习PhysicsEditor之前,先讲一下它是uhe跟Flash一起工作的。学习新事物就要和我们熟悉的东西联系到一起,这样才能理解的更快,不是吗?!大约有下面几个步骤:
- 在PhysicsEditor中创建好刚体模型后(怎么创建,稍后再讲),导出成一个PhysicsData.as类。这个类中有一个createBody方法,用来创建刚体,以及保存了刚体的顶点数据。
- 把在PhysicsEditor中用来创建刚体模型的图像导入到Flash中,稍后作为刚体的userData使用。当然,如果不需要userData,可以省略这一步。
- 创建文档类,键入代码,创建一个基本的Box2D世界。
- 在文档类中,创建一个PhysicsData实例对象,用下面的代码创建刚体。然后正常的更新Box2D世界就可以了。
以拉登大叔的头像为例,实现的效果如下,点击舞台创建头像。
具体的用PhysicsEditor创建刚体模型和导入PhysicsData类的方法,我们下一节继续介绍。上面示例的完整代码和注释如下:
|
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
|
package
{
importBox2D.Collision.b2WorldManifold;
importBox2D.Common.Math.b2Vec2;
importBox2D.Dynamics.b2Body;
importBox2D.Dynamics.b2World;
importBox2D.Dynamics.Contacts.b2Contact;
importflash.display.Bitmap;
importflash.display.Sprite;
importflash.events.Event;
importflash.events.MouseEvent;
/**
* ...
* @author ladeng6666
*/
publicclassMainextendsSprite
{
privatevarworld:b2World;
privatevarphyicsData:PhysicsData;
publicfunctionMain()
{
createWorld();//创建世界
phyicsData=newPhysicsData();
//添加事件侦听器
addEventListener(Event.ENTER_FRAME,loop);
stage.addEventListener(MouseEvent.MOUSE_DOWN,onMouseDown);
}
//创建Box2D世界
privatefunctioncreateWorld():void
{
//创建Box2D世界
world=world=newb2World(newb2Vec2(0,10),true);
//添加调试视图
addChild(LDEasyBox2D.createDebug(world));
LDEasyBox2D.stage=stage;
LDEasyBox2D.createWrapWall(world,this);
}
privatefunctiononMouseDown(me:MouseEvent):void
{
//用拉登大叔的头像创建userData,并添加到舞台上
varuserData:Bitmap=newBitmap(newladeng6666(0,0));
addChild(userData);
//用physicsData实例的createBody方法创建刚体,参数的重点是第一个
//它是在PhysicsEditor中添加的图像的名称,不要Flash库里的图像名称混为一谈
//现在不明白也没关系,下一节我们会仔细学习
varbody:b2Body=phyicsData.createBody("ladeng6666",world,b2Body.b2_dynamicBody,userData);
//设置刚体的坐标
varposition:b2Vec2=newb2Vec2(mouseX/phyicsData.ptm_ratio,mouseY/phyicsData.ptm_ratio);
position.Subtract(body.GetLocalCenter());
body.SetPosition(position);
}
privatefunctionloop(e:Event):void
{
varbody:b2Body=world.GetBodyList();
//遍历所有的刚体,更新它们的userData的坐标个角度
for(;body;body=body.GetNext()){
if(body.GetUserData()!=null){
body.GetUserData().x=body.GetPosition().x*phyicsData.ptm_ratio;
body.GetUserData().y=body.GetPosition().y*phyicsData.ptm_ratio;
body.GetUserData().rotation=body.GetAngle()*180/Math.PI;
}
}
LDEasyBox2D.updateWorld(world);
}
}
}
|
源文件下载地址
上一节,我们认识了PhysicsEditor(后面称为PE)这么一个用来创建自定义Box2D刚体的工具,我们知道了他会自动生成一个PhysicsData.as类,通过PhysicsData的createBody()方法就可以轻松创建多边形刚体,今天我们就学习一下,如果使用PE来生成PhysicsData类,并应用到Flash中。
首先到PE的官网下载并安装这个软件,PE默认是试用版,开启后延迟激活,而限制只能创建由10以下刚体组成的多边形,不过对于我们学习来说,足够了。
安装好后,开启PhysicsEditor,看到的初始界面如下:
图1.PE开启后的界面
默认情况下,开启时编辑区是空白的,是没有拉登大叔的头像的,这一点说明一下啊!看到我做的标记了没?我们就按照这个顺序来看看PE怎么用。
点击1处的按钮,任意选择一张图片,尽量使用扣去背景色后存储为.png或.gif图片,因为这两种格式都支持透明像素,PE可以自动忽略透明像素。例如拉登大叔的头像其实是有背景的,扣掉背景后才有上面的效果。
图片添加成功后,会出现的3的位置,点击2处的按钮,可以删除已经添加了的图像。
添加完图片后,我们可以点击4处的按钮,让PE自动帮忙捕获图片的边缘,自动生成多边形顶点信息。当然也可以自己动手点击5或6位置的按钮,添加多边形或圆形组合成想要的效果。点击5处的按钮,默认会生成一个三角形,在任意两个顶点之间双击,可以添加新的顶点。相比之下,4处的按钮就简单多了,点击该按钮后会出现如图2所示的界面。
图2.点击6处的shape tracer按钮后,弹出对话框,PE自动追踪图像边缘
上图中的8处所示,是PE自动追踪图片边缘的效果。9处可以设置顶点之间的距离,这个值越小,顶点间距越小,多边形越接近图片的形状,同时消耗CPU也越多,所以不必过度要求多边形的精度,大体形状差不多就行。9处设置好后,PE会自动计算出顶点的个数,并显示在10处。
图2的对话框设置好后,点击OK按钮,返回到图1界面后,拉登大叔脑袋周围就出现了PE计算好的顶点,数数是不是跟10处显示的一样?
接下来我们再看看右边信息栏里的内容:
图3.多边形刚体创建好后,PE会在右侧显示出相关的信息
PE帮我们追踪完图片边缘后,右侧会显示该多边形的信息。11处是像素与Box2D中的m之间的比例,默认为32,但是我们一直用的都是30。12处是刚体的弹性、摩擦力、密度、isSensor信息,和b2Fixture里是对应的。13处是组成该多边形的子刚体的信息,每个刚体后面的选项是指碰撞过滤信息,Cat是指category刚体碰撞分组,Mask指刚体碰撞过滤,具体可以参考FilterData让Box2D碰撞分类进行这篇教程。
用同样的方法,我们还可以继续添加其他的图像,如下:
图4.添加多个图像
上面的步骤都完成后,选择图1中的7处为"Box2D ActionScript(Flash)",然后点击左上方的Publish按钮,就可以导出PhysicsData类了。
下面是我添加了3个图像之后的效果,点击舞台任意位置,随即创建3个图像的其中之一。
我生成的PhysicsData类如下:
|
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
|
package
{
importBox2D.Dynamics.*;
importBox2D.Collision.*;
importBox2D.Collision.Shapes.*;
importBox2D.Common.Math.*;
importflash.utils.Dictionary;
publicclassPhysicsDataextendsObject
{
// ptm ratio
publicvarptm_ratio:Number=30;
// the physcis data
vardict:Dictionary;
//
// bodytype:
// b2_staticBody
// b2_kinematicBody
// b2_dynamicBody
publicfunctioncreateBody(name:String,world:b2World,bodyType:uint,userData:*):b2Body
{
varfixtures:Array=dict[name];
varbody:b2Body;
varf:Number;
// prepare body def
varbodyDef:b2BodyDef=newb2BodyDef();
bodyDef.type=bodyType;
bodyDef.userData=userData;
// create the body
body=world.CreateBody(bodyDef);
// prepare fixtures
for(f=0;f<fixtures.length;f++)
{
varfixture:Array=fixtures[f];
varfixtureDef:b2FixtureDef=newb2FixtureDef();
fixtureDef.density=fixture[0];
fixtureDef.friction=fixture[1];
fixtureDef.restitution=fixture[2];
fixtureDef.filter.categoryBits=fixture[3];
fixtureDef.filter.maskBits=fixture[4];
fixtureDef.filter.groupIndex=fixture[5];
fixtureDef.isSensor=fixture[6];
if(fixture[7]=="POLYGON")
{
varp:Number;
varpolygons:Array=fixture[8];
for(p=0;p<polygons.length;p++)
{
varpolygonShape:b2PolygonShape=newb2PolygonShape();
polygonShape.SetAsArray(polygons[p],polygons[p].length);
fixtureDef.shape=polygonShape;
body.CreateFixture(fixtureDef);
}
}
elseif(fixture[7]=="CIRCLE")
{
varcircleShape:b2CircleShape=newb2CircleShape(fixture[9]);
circleShape.SetLocalPosition(fixture[8]);
fixtureDef.shape=circleShape;
body.CreateFixture(fixtureDef);
}
}
returnbody;
}
publicfunctionPhysicsData():void
{
dict=newDictionary();
dict["ladeng6666"]=[
[
// density, friction, restitution
2,3,0.2,
// categoryBits, maskBits, groupIndex, isSensor
1,65535,0,false,
'POLYGON',
// vertexes of decomposed polygons
[
[ newb2Vec2(80/ptm_ratio,82/ptm_ratio) , newb2Vec2(0/ptm_ratio,82/ptm_ratio) , newb2Vec2(69/ptm_ratio,62/ptm_ratio) , newb2Vec2(80/ptm_ratio,68/ptm_ratio) ],
[ newb2Vec2(64/ptm_ratio,53/ptm_ratio) , newb2Vec2(26/ptm_ratio,26/ptm_ratio) , newb2Vec2(29/ptm_ratio,9/ptm_ratio) , newb2Vec2(36/ptm_ratio,1/ptm_ratio) , newb2Vec2(56/ptm_ratio,0/ptm_ratio) , newb2Vec2(77/ptm_ratio,24/ptm_ratio) , newb2Vec2(77/ptm_ratio,36/ptm_ratio) , newb2Vec2(72/ptm_ratio,52/ptm_ratio) ],
[ newb2Vec2(9/ptm_ratio,73/ptm_ratio) , newb2Vec2(0/ptm_ratio,82/ptm_ratio) , newb2Vec2(4/ptm_ratio,74/ptm_ratio) ],
[ newb2Vec2(77/ptm_ratio,24/ptm_ratio) , newb2Vec2(56/ptm_ratio,0/ptm_ratio) , newb2Vec2(67/ptm_ratio,3/ptm_ratio) ],
[ newb2Vec2(29/ptm_ratio,61/ptm_ratio) , newb2Vec2(9/ptm_ratio,73/ptm_ratio) , newb2Vec2(12/ptm_ratio,68/ptm_ratio) ],
[ newb2Vec2(0/ptm_ratio,82/ptm_ratio) , newb2Vec2(9/ptm_ratio,73/ptm_ratio) , newb2Vec2(29/ptm_ratio,61/ptm_ratio) , newb2Vec2(64/ptm_ratio,53/ptm_ratio) , newb2Vec2(69/ptm_ratio,62/ptm_ratio) ],
[ newb2Vec2(26/ptm_ratio,26/ptm_ratio) , newb2Vec2(64/ptm_ratio,53/ptm_ratio) , newb2Vec2(29/ptm_ratio,61/ptm_ratio) , newb2Vec2(23/ptm_ratio,28/ptm_ratio) ]
]
]
];
dict["drink"]=[
[
// density, friction, restitution
2,3,0.2,
// categoryBits, maskBits, groupIndex, isSensor
1,65535,0,false,
'POLYGON',
// vertexes of decomposed polygons
[
[ newb2Vec2(13/ptm_ratio,5/ptm_ratio) , newb2Vec2(41/ptm_ratio,28/ptm_ratio) , newb2Vec2(34/ptm_ratio,31/ptm_ratio) , newb2Vec2(8/ptm_ratio,11/ptm_ratio) , newb2Vec2(8/ptm_ratio,6/ptm_ratio) ],
[ newb2Vec2(13/ptm_ratio,65/ptm_ratio) , newb2Vec2(39/ptm_ratio,62/ptm_ratio) , newb2Vec2(14/ptm_ratio,80/ptm_ratio) , newb2Vec2(11/ptm_ratio,72/ptm_ratio) ],
[ newb2Vec2(67/ptm_ratio,169/ptm_ratio) , newb2Vec2(58/ptm_ratio,172/ptm_ratio) , newb2Vec2(32/ptm_ratio,172/ptm_ratio) , newb2Vec2(23/ptm_ratio,170/ptm_ratio) , newb2Vec2(39/ptm_ratio,62/ptm_ratio) , newb2Vec2(47/ptm_ratio,61/ptm_ratio) , newb2Vec2(77/ptm_ratio,65/ptm_ratio) ],
[ newb2Vec2(39/ptm_ratio,62/ptm_ratio) , newb2Vec2(23/ptm_ratio,170/ptm_ratio) , newb2Vec2(14/ptm_ratio,80/ptm_ratio) ],
[ newb2Vec2(41/ptm_ratio,28/ptm_ratio) , newb2Vec2(47/ptm_ratio,61/ptm_ratio) , newb2Vec2(40/ptm_ratio,57/ptm_ratio) , newb2Vec2(34/ptm_ratio,31/ptm_ratio) ],
[ newb2Vec2(47/ptm_ratio,61/ptm_ratio) , newb2Vec2(39/ptm_ratio,62/ptm_ratio) , newb2Vec2(40/ptm_ratio,57/ptm_ratio) ]
]
]
];
dict["icecream2"]=[
[
// density, friction, restitution
2,3,0.2,
// categoryBits, maskBits, groupIndex, isSensor
1,65535,0,false,
'POLYGON',
// vertexes of decomposed polygons
[
[ newb2Vec2(44/ptm_ratio,19/ptm_ratio) , newb2Vec2(44/ptm_ratio,70/ptm_ratio) , newb2Vec2(38/ptm_ratio,73/ptm_ratio) , newb2Vec2(30/ptm_ratio,72/ptm_ratio) , newb2Vec2(26/ptm_ratio,71/ptm_ratio) , newb2Vec2(25/ptm_ratio,11/ptm_ratio) , newb2Vec2(33/ptm_ratio,6/ptm_ratio) ],
[ newb2Vec2(11/ptm_ratio,73/ptm_ratio) , newb2Vec2(19/ptm_ratio,73/ptm_ratio) , newb2Vec2(19/ptm_ratio,97/ptm_ratio) , newb2Vec2(11/ptm_ratio,98/ptm_ratio) ],
[ newb2Vec2(3/ptm_ratio,68/ptm_ratio) , newb2Vec2(2/ptm_ratio,19/ptm_ratio) , newb2Vec2(11/ptm_ratio,8/ptm_ratio) , newb2Vec2(25/ptm_ratio,11/ptm_ratio) , newb2Vec2(26/ptm_ratio,71/ptm_ratio) , newb2Vec2(19/ptm_ratio,73/ptm_ratio) , newb2Vec2(11/ptm_ratio,73/ptm_ratio) , newb2Vec2(4/ptm_ratio,71/ptm_ratio) ],
[ newb2Vec2(31/ptm_ratio,98/ptm_ratio) , newb2Vec2(30/ptm_ratio,72/ptm_ratio) , newb2Vec2(38/ptm_ratio,73/ptm_ratio) , newb2Vec2(38/ptm_ratio,98/ptm_ratio) ],
[ newb2Vec2(25/ptm_ratio,11/ptm_ratio) , newb2Vec2(11/ptm_ratio,8/ptm_ratio) , newb2Vec2(17/ptm_ratio,7/ptm_ratio) ]
]
]
];
}
}
}
|
主文档类的代码和上一节里的差不多,我就不再赘述了,具体请下载源文件查看。
源文件下载地址
posted on 2014-03-23 22:12 shengchang 阅读(611) 评论(0) 收藏 举报





浙公网安备 33010602011771号