为满足项目需求,我用java写了一个生成Box2D b2PolygonShape多边形顶点的工具。
也是一步一步完成的,首先是为1个不规则的sprite图片生成多边形shape轮廓,后来发现如果用Box2D里面的compound概念的话
可以生成比较完美的不规则图片 shape 轮廓,因此,工具升级到1.1,从此支持对不规则的sprite图片进行描点
1.1版完成的时候逛了逛子龙山人的博客,发现ray的教程里面有介绍1个多边形描点工具
是源自 Johannes Fahrenkrug 的 VertexHelper,下下来在xcode里面跑了下,
发现还不错,
不过让我比较不解的是:生成的竟然是 box2d 以及 chipmunk 引擎的程序代码片段
这个我感觉不是很好,灵活性不够!不过这都是后话了,其实我对自己写的描点工具还是算比较满意的~
下面给个VertexHelper的图:
下面是VertextHelper的用例图,相信还是很容易就能看出怎么使用这个工具的,如果你用过Box2D的话~
以下是我做的工具的几张截图:
这个工具的设计思路还是比较简单的:
①将复杂的图形分割成多个凸多边形(Box2D只支持凸多边形,而且最大8个顶点,如果要超过8个顶点,请修改b2Settings.h头文件)
②这张图片我进行了倒置变换,这样可以减少一些坐标变换的工作,不过也带来了一些不好的影响,用到了一个类结果导致打不成Jar包了
③鉴于对JFrame边框以及Javaswing空间尺寸打不准,因此程序界面尽量简洁免除不必要的工作量,功能输入由快捷键代劳
比如,可以动态更改所描点的尺寸([]),以及子多边形外围矩形包围框的宽度(<>)
④避免输入不合乎条件的点,我对整个图片加了一个矩形包围框,点在这个矩形包围框外部的点将无效
⑤输入难免会出现差错,而且一个复杂图形必定要点出很多个子凸多边形才能完全覆盖,这样的话,出现错误时的挽救就是个问题
我采用了这样的思路:动画sprite图片最多不超过512*512的尺寸,那么,对付这么一些小个子图片的话
适当的采用高清4倍版的图片进行描点必会带来很好的用户体验,这样的话,就不用因为图片实在是太小点不准而做重复的工作了~
其实在第1版仅仅为1整张图片描出一个多边形的时候,我加入了撤销的操作,但是1.1版因为加入了复合多边形的概念
一张图片将会由多个子凸多边形来覆盖,撤销操作难免要考虑到不少的情形,我直接屏蔽之了,其实我是个懒人~
对于bird1_animi0×0.5.png这张图片
下面是生成的数据文件格式:
bird1_animi=0-59,18-22,41-41*41-41,18-22,30-8,50-1,78-6,69-45*69-45,78-6,93-24,99-44,100-57,86-59*86-59,100-57,95-84,81-81
bird2_animi=1-57,13-19,30-6,53-0,75-2,35-46*35-46,75-2,102-11,112-28,114-48,111-64,69-74*69-74,111-64,139-86,87-132,75-128,65-99
lighttower_animi=4-2,247-5,161-537,81-536*81-536,161-537,169-612,73-610*73-610,169-612,183-622,124-709,60-618
数据格式很简单,也很容易进行解析:
①采用了键值对的方式,可以通过以=分割成字符串数组,很容易的将一个个的sprite的形状数据添加到NSMutableDictionary里面去;
②多个形状图片之间以\n换行符进行分隔(windows用\r\n换行,linux,mac,java仅以\n换行),多个子凸多边形之间以星号进行分隔,
单个子凸多边形的各个顶点之间以逗号进行分隔,单个顶点的横纵坐标之间以横线进行分隔~
接下来是解析和使用该数据格式的相关代码:
①初步解析数据以字典的形式保存起来(该字典对象是一个单例,初步解析后保存的数据可以在整个程序的范围内被访问到)~
// 加载sprite动画的顶点信息~
-(void) loadAnimationData {
_animVerticesDic = [[NSMutableDictionary alloc] initWithCapacity:20];
NSString *absPath = [[NSBundle mainBundle] pathForResource:@"animVerticesSummary" ofType:@"txt"];
NSFileManager *fm = [NSFileManager defaultManager];
if([fm fileExistsAtPath:absPath] == NO) {
NSLog(@"File not exists!");
return;
}
NSString *fileContent = [NSString stringWithContentsOfFile:absPath
encoding:NSUTF8StringEncoding
error:nil];
NSArray *verticesSummaryArray = [fileContent componentsSeparatedByString:@"\n"];
int len = [verticesSummaryArray count];
for(int i = 0; i < len; i ++) {
NSString *unit = [verticesSummaryArray objectAtIndex:i];
NSRange range = [unit rangeOfString:@"="];
NSString *keyStr = [unit substringToIndex:range.location];
NSString *valueStr = [unit substringFromIndex:range.location+1];
[_animVerticesDic setObject:valueStr forKey:keyStr];
}
}
②具体的数据解析代码:
// 解析包含sprite动画帧的顶点数据的字符串
- (void) createSpriteShapes:(NSString*)animName sz:(CGSize)size {
NSString *verticesStr = [[BYSingle getInstance].animVerticesDic objectForKey:animName];
NSArray *childShapes = [verticesStr componentsSeparatedByString:@"*"];
_childShapeCount = [childShapes count];
NSLog(@"有多少个子形状: %d", _childShapeCount);
_b2Shapes = new b2PolygonShape[_childShapeCount];
_b2FixtureDefs = new b2FixtureDef[_childShapeCount];
for(int i = 0; i < _childShapeCount; i ++) {
NSString *verticesStr = [childShapes objectAtIndex:i];
NSArray *verticesArray = [verticesStr componentsSeparatedByString:@","];
int verticesLen = [verticesArray count];
NSLog(@"第%d个子形状有多少个顶点: %d", i, verticesLen);
b2Vec2 vertices[verticesLen];
for(int j = 0; j < verticesLen; j ++) {
NSString *vStr = [verticesArray objectAtIndex:j];
NSArray *array = [vStr componentsSeparatedByString:@"-"];
float x = (float)[[array objectAtIndex:0] intValue];
float y = (float)[[array objectAtIndex:1] intValue];
vertices[j] = b2Vec2(x/PTM_RATIO, y/PTM_RATIO);
}
for(int j = 0; j < verticesLen; j ++) {
vertices[j]+=b2Vec2(-size.width/2/PTM_RATIO, -size.height/2/PTM_RATIO); // 平移使图片与polygon重合~
}
_b2Shapes[i].Set(vertices, verticesLen);
_b2FixtureDefs[i].shape = &_b2Shapes[i];
_b2FixtureDefs[i].density = BOX_DENSITY;
_b2FixtureDefs[i].friction = BOX_FRICTION;
_b2FixtureDefs[i].restitution = BOX_RESTITUTION;
}
}
抹了,贴几张游戏的图片
①单个凸多边形版的:
②多个子凸多边形版的: