Created
March 21, 2014 04:05
-
-
Save gintsmurans/9679330 to your computer and use it in GitHub Desktop.
Cocos2d v3 Physics Editor shape loader
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
<plist version="1.0"> | |
<dict> | |
<key>metadata</key> | |
<dict> | |
<key>format</key> | |
<integer>1</integer> | |
<key>designScale</key> | |
<real>{{global.designScale}}</real> | |
</dict> | |
<key>bodies</key> | |
<dict> {% for body in bodies %} | |
<key>{{body.name}}</key> | |
<dict> | |
<key>size</key> | |
<string>{ {{body.size.width|floatformat:5}},{{body.size.height|floatformat:5}} }</string> | |
<key>anchorpoint</key> | |
<string>{ {{body.anchorPointRel.x|floatformat:5}},{{body.anchorPointRel.y|floatformat:5}} }</string> | |
<key>physicsBodyTypeDynamic</key> | |
{% if body.physicsBodyTypeDynamic %}<true/>{% else %}<false/>{% endif %} | |
<key>canRotate</key> | |
{% if body.canRotate %}<true/>{% else %}<false/>{% endif %} | |
<key>obeyGravity</key> | |
{% if body.obeyGravity %}<true/>{% else %}<false/>{% endif %} | |
<key>fixtures</key> | |
<array> {% for fixture in body.fixtures %} | |
<dict> | |
<key>mass</key> | |
<real>{{fixture.mass}}</real> | |
<key>elasticity</key> | |
<real>{{fixture.elasticity}}</real> | |
<key>friction</key> | |
<real>{{fixture.friction}}</real> | |
<key>surface_velocity</key> | |
<string>{ {{fixture.surface_velocity_x|floatformat:5}},{{fixture.surface_velocity_y|floatformat:5}} }</string> | |
<key>collisionGroup</key> | |
<string>{{fixture.collision_group}}</string> | |
<key>collisionType</key> | |
<string>{{fixture.collision_type}}</string> | |
<key>isSensor</key> | |
{% if fixture.isSensor %}<true/>{% else %}<false/>{% endif %} | |
<key>fixture_type</key> | |
<string>{{fixture.type}}</string> | |
{% if fixture.isCircle %} | |
<key>circle</key> | |
<dict> | |
<key>radius</key> | |
<real>{{fixture.radius|floatformat:3}}</real> | |
<key>position</key> | |
<string>{ {{fixture.center.x|floatformat:3}},{{fixture.center.y|floatformat:3}} }</string> | |
</dict> | |
{% else %} | |
<key>isPolylines</key> | |
{% if fixture.isPolylines %}<true/>{% else %}<false/>{% endif %} | |
<key>cornerRadius</key> | |
<real>{{fixture.cornerRadius}}</real> | |
<key>hull</key> | |
<array>{% for point in fixture.hull %} | |
<string>{ {{point.x|floatformat:5}},{{point.y|floatformat:5}} }</string>{% endfor %} | |
</array> | |
<key>polygons</key> | |
<array>{% for polygon in fixture.polygons %} | |
<array>{% for point in polygon %} | |
<string>{ {{point.x|floatformat:5}},{{point.y|floatformat:5}} }</string>{% endfor %} | |
</array>{% endfor %} | |
</array> | |
{% endif %} | |
</dict> {% endfor %} | |
</array> | |
</dict> {% endfor %} | |
</dict> | |
</dict> | |
</plist> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0"?> | |
<exporter> | |
<name>ccphysics</name> | |
<displayName>Cocos2D v3.X CCPhysics</displayName> | |
<description>Exporter for generating physics data for CCPhysics in Cocos2d v3.X.</description> | |
<version>0.1</version> | |
<yAxisDirection>up</yAxisDirection> | |
<physicsEngine>chipmunk</physicsEngine> | |
<template>ccphysics.plist</template> | |
<fileExtension>plist</fileExtension> | |
<anchorPoint> | |
<enabled>yes</enabled> | |
<relX>0.5</relX> | |
<relY>0.5</relY> | |
</anchorPoint> | |
<origin> | |
<type>anchorPoint</type> | |
</origin> | |
<global> | |
<parameter> | |
<name>designScale</name> | |
<displayName>Design scale?</displayName> | |
<description>Indicates the scale of the design (retina/non-retina).</description> | |
<type>float</type> | |
<default>2.0</default> | |
</parameter> | |
</global> | |
<body> | |
<parameter> | |
<name>physicsBodyTypeDynamic</name> | |
<displayName>Dynamic Body</displayName> | |
<description>Indicates whether body is dynamic or static</description> | |
<type>bool</type> | |
<default>false</default> | |
</parameter> | |
<parameter> | |
<name>canRotate</name> | |
<displayName>Able to Rotate?</displayName> | |
<description>Sets ability for body/shape to rotate</description> | |
<type>bool</type> | |
<default>true</default> | |
</parameter> | |
<parameter> | |
<name>obeyGravity</name> | |
<displayName>Affected by Gravity?</displayName> | |
<description>Indicates if this body/shape respects gravity</description> | |
<type>bool</type> | |
<default>true</default> | |
</parameter> | |
</body> | |
<fixture> | |
<parameter> | |
<name>mass</name> | |
<displayName>Mass</displayName> | |
<type>float</type> | |
<default>2.0</default> | |
</parameter> | |
<parameter> | |
<name>elasticity</name> | |
<displayName>Elasticity</displayName> | |
<type>float</type> | |
<min>0</min> | |
<max>1000</max> | |
<default>0.0</default> | |
</parameter> | |
<parameter> | |
<name>friction</name> | |
<displayName>Friction</displayName> | |
<type>float</type> | |
<min>0</min> | |
<max>1000</max> | |
<default>0.0</default> | |
</parameter> | |
<parameter> | |
<name>surface_velocity_x</name> | |
<displayName>Surface velocity X</displayName> | |
<type>float</type> | |
<min>-1000</min> | |
<max>1000</max> | |
<default>0.0</default> | |
</parameter> | |
<parameter> | |
<name>surface_velocity_y</name> | |
<displayName>Surface velocity Y</displayName> | |
<type>float</type> | |
<min>-1000</min> | |
<max>1000</max> | |
<default>0.0</default> | |
</parameter> | |
<parameter> | |
<name>cornerRadius</name> | |
<displayName>Corner Radius</displayName> | |
<type>float</type> | |
<min>-1000</min> | |
<max>1000</max> | |
<default>0.0</default> | |
</parameter> | |
<parameter> | |
<name>isSensor</name> | |
<displayName>Is Sensor</displayName> | |
<description>If set the polygon is a sensor</description> | |
<type>bool</type> | |
<default>false</default> | |
</parameter> | |
<parameter> | |
<name>isPolylines</name> | |
<displayName>Is Polylines</displayName> | |
<description>If set the polygon will be from polyLines</description> | |
<type>bool</type> | |
<default>false</default> | |
</parameter> | |
<parameter> | |
<name>collision_type</name> | |
<displayName>Collision Type</displayName> | |
<description>Collision Type</description> | |
<shortDescription>Collision Type</shortDescription> | |
<type>string</type> | |
<default>default</default> | |
</parameter> | |
<parameter> | |
<name>collision_group</name> | |
<displayName>Collision Group</displayName> | |
<description>Collision Group</description> | |
<shortDescription>Collision Group</shortDescription> | |
<type>string</type> | |
<default>nil</default> | |
</parameter> | |
</fixture> | |
</exporter> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// PEShapeCache.h | |
// | |
// Put together form the sources all over the internet by Gints Murans. | |
// No guarantees, of course. | |
// | |
/* | |
This class is used to load physics shapes from a plist created by the | |
Physics Editor. It will work only when used together with a custom exporter. | |
Custom exporters can be added to Physics Editor in its Preferences. | |
*/ | |
#import "cocos2d.h" | |
typedef enum { | |
POLYGON_FIXTURE, | |
CIRCLE_FIXTURE, | |
POLYLINE_FIXTURE | |
} FixtureType; | |
#pragma mark - CCNode (PhysicsEditor) | |
@interface CCNode (PhysicsEditor) | |
// Shortcut to add physics body to a CCNode | |
- (void)setPhysicsBodyWithName:(NSString *)name; | |
@end | |
#pragma mark - PEShapeCache | |
@interface PEShapeCache : NSObject | |
// ShapeCache is a singleton, and this will return that single instance | |
+(PEShapeCache*)sharedShapeCache; | |
// reads the plist created with PE and creates a dictionary of body definitions | |
// with shape definitions in tow | |
-(BOOL)addPhysicsShapesWithFile:(NSString*)plist; | |
// Returns a CCPhysicsBody with accompanying shapes as well as other physics | |
// attributes | |
-(CCPhysicsBody*)bodyWithName:(NSString*)name; | |
// Returns the anchor point for use with a CCSprite | |
-(CGPoint)anchorPointForShape:(NSString*)shape; | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// PEShapeCache.m | |
// | |
// Put together form the sources all over the internet by Gints Murans. | |
// No guarantees, of course. | |
// | |
#import "PEShapeCache.h" | |
#pragma mark - CCNode (PhysicsEditor) | |
@implementation CCNode (PhysicsEditor) | |
- (void)setPhysicsBodyWithName:(NSString *)name | |
{ | |
[self setPhysicsBody:[[PEShapeCache sharedShapeCache] bodyWithName:name]]; | |
[self setAnchorPoint:[[PEShapeCache sharedShapeCache] anchorPointForShape:name]]; | |
} | |
@end | |
static float area(CGPoint* verts, int vertCount) { | |
int n = (vertCount-1); | |
// calculate triangle between last and first vert | |
float area = (verts[0].x* verts[n].y) - (verts[n].x * verts[0].y); | |
// calculate each quad individually and add to area | |
for (int i=0; i<vertCount-1; ++i) { | |
area += (verts[n-i].x * verts[n-(i+1)].y) - (verts[n-(i+1)].x * verts[n-i].y); | |
} | |
// multiply by half to get the triangle area from the quad | |
area = area * 0.5f; | |
return area; | |
} | |
#pragma mark - Polygon | |
//********************************************************************** | |
// Holds details on individual polygons that make up shape | |
@interface Polygon : NSObject | |
@property CGPoint* vertices; | |
@property int numVertices; | |
@property float area; | |
@end | |
//************ | |
@implementation Polygon | |
@end | |
#pragma mark - FixtureNode | |
//********************************************************************* | |
// Fixture definition to hold fixture data. Most attributes are stored | |
//here, as well as polygons and hull verts. | |
@interface FixtureNode : NSObject | |
@property FixtureType fixtureType; | |
@property float mass; | |
@property float density; | |
@property float area; | |
@property float elasticity; | |
@property float friction; | |
@property CGPoint surfaceVelocity; | |
@property NSString* collisionType; | |
@property NSString* collisionGroup; | |
@property BOOL isSensor; | |
@property float cornerRadius; | |
// for circles | |
@property CGPoint center; | |
@property float radius; | |
// for solid polygons | |
@property NSMutableArray* polygons; | |
// for polygons made of polyLines | |
@property BOOL isPolylines; | |
@property CGPoint* polyPoints; | |
@property int numPolyPoints; | |
@end | |
//*************** | |
@implementation FixtureNode | |
-(id)init { | |
self = [super init]; | |
if (!self) return nil; | |
self.polygons = [NSMutableArray array]; | |
return self; | |
} | |
@end | |
#pragma mark - BodyNode | |
//*********************************************************************** | |
// Body definition holds the ficture data, as well as booleans telling | |
// us if the body can rotate and respects gravity. Also has anchor point for | |
// use when lining up a sprite | |
@interface BodyNode : NSObject | |
@property CGPoint anchorPoint; | |
@property NSMutableArray* fixtures; | |
@property BOOL canRotate; | |
@property BOOL obeyGravity; | |
@property BOOL dynamicBody; | |
@end | |
//************ | |
@implementation BodyNode | |
-(id)init { | |
self = [super init]; | |
if (!self) return nil; | |
self.fixtures = [NSMutableArray array]; | |
return self; | |
} | |
@end | |
#pragma mark - PEShapeCache | |
//*********************************************************************** | |
// this is where all the body definitions are stored once the plist | |
// has been read and parsed. | |
@implementation PEShapeCache { | |
NSMutableDictionary* _bodyNodes; | |
} | |
static PEShapeCache* shapeCache = nil; | |
-(id)init { | |
self = [super init]; | |
if (!self) return nil; | |
_bodyNodes = [[NSMutableDictionary alloc] init]; | |
return self; | |
} | |
+(PEShapeCache*)sharedShapeCache { | |
if(shapeCache == nil) shapeCache = [[PEShapeCache alloc] init]; | |
return shapeCache; | |
} | |
// returns anchor point for use with a corresponding sprite | |
-(CGPoint)anchorPointForShape:(NSString*)shape { | |
BodyNode *bn = [_bodyNodes objectForKey:shape]; | |
if (!bn) { | |
CCLOG(@"Body not found for shape: %@",shape); | |
return CGPointZero; | |
} | |
return bn.anchorPoint; | |
} | |
// Meat and potatoes here. Returns a fully set up CCPhysicsBody, which also | |
// has its shapes attached. This method creates the bodies as CCPhysicsShapes | |
// and uses the bodywithshapes: method for creating the body. Most attributes | |
// are implemented through the shape, except for allowsRotation and | |
// affectedByGravity, which are exclusive to CCPhysicsBody | |
-(CCPhysicsBody*)bodyWithName:(NSString*)name { | |
BodyNode* bn = [_bodyNodes objectForKey:name]; | |
if (!bn) { | |
CCLOG(@"Body not found for name: %@",name); | |
return nil; | |
} | |
// it puts the lotion (shapes) in the basket (array) | |
NSMutableArray* shapes = [NSMutableArray array]; | |
// iterate over fixtures | |
for (FixtureNode* fn in bn.fixtures) { | |
if (fn.fixtureType == CIRCLE_FIXTURE) { | |
CCPhysicsShape* shape = [CCPhysicsShape circleShapeWithRadius:fn.radius | |
center:fn.center]; | |
// set property values | |
shape.mass = fn.mass; | |
shape.elasticity = fn.elasticity; | |
shape.friction = fn.friction; | |
shape.surfaceVelocity = fn.surfaceVelocity; | |
shape.sensor = fn.isSensor; | |
shape.collisionType = fn.collisionType; | |
shape.collisionGroup = NSClassFromString(fn.collisionGroup); | |
[shapes addObject:shape]; | |
} else if (fn.fixtureType == POLYGON_FIXTURE) { | |
// iterate over polygons | |
for (Polygon* poly in fn.polygons) { | |
CCPhysicsShape* shape = [CCPhysicsShape polygonShapeWithPoints:poly.vertices | |
count:poly.numVertices | |
cornerRadius:fn.cornerRadius]; | |
// this one is different in that in order to keep the mass the | |
// original amount, I calculated a density and apply that to the area | |
// of each polygon in order to | |
shape.mass = poly.area * fn.density; | |
shape.elasticity = fn.elasticity; | |
shape.friction = fn.friction; | |
shape.surfaceVelocity = fn.surfaceVelocity; | |
shape.sensor = fn.isSensor; | |
shape.collisionType = fn.collisionType; | |
shape.collisionGroup = NSClassFromString(fn.collisionGroup); | |
[shapes addObject:shape]; | |
} | |
} else if (fn.fixtureType == POLYLINE_FIXTURE) { | |
for (int i = 0; i < fn.numPolyPoints; i++) { | |
CGPoint point1 = fn.polyPoints[i]; | |
CGPoint point2 = fn.polyPoints[i+1]; | |
if (i + 1 == fn.numPolyPoints) point2 = fn.polyPoints[0]; | |
CCPhysicsShape* shape = [CCPhysicsShape pillShapeFrom:point1 | |
to:point2 | |
cornerRadius:fn.cornerRadius]; | |
shape.mass = fn.mass; | |
shape.elasticity = fn.elasticity; | |
shape.friction = fn.friction; | |
shape.surfaceVelocity = fn.surfaceVelocity; | |
shape.sensor = fn.isSensor; | |
shape.collisionType = fn.collisionType; | |
shape.collisionGroup = NSClassFromString(fn.collisionGroup); | |
[shapes addObject:shape]; | |
} | |
} | |
} | |
CCPhysicsBody* body = [CCPhysicsBody bodyWithShapes:shapes]; | |
body.allowsRotation = bn.canRotate; | |
body.affectedByGravity = bn.obeyGravity; | |
body.type = (bn.dynamicBody ? CCPhysicsBodyTypeDynamic : CCPhysicsBodyTypeStatic); | |
return body; | |
} | |
-(BOOL)addPhysicsShapesWithFile:(NSString*)plist{ | |
NSString *path = [[NSBundle mainBundle] pathForResource:plist ofType:nil inDirectory:nil]; | |
NSDictionary* dictionary = [NSDictionary dictionaryWithContentsOfFile:path]; | |
if(!dictionary) { | |
CCLOG(@"Unable to load file: %@", plist); | |
return FALSE; | |
} | |
NSDictionary *metadataDict = [dictionary objectForKey:@"metadata"]; | |
int format = [[metadataDict objectForKey:@"format"] intValue]; | |
if(format != 1) { | |
CCLOG(@"Format not supported"); | |
return FALSE; | |
} | |
float designScale = [[metadataDict objectForKey:@"designScale"] floatValue]; | |
NSDictionary* bodyDict = [dictionary objectForKey:@"bodies"]; | |
for(NSString* bodyName in bodyDict) { | |
// get the body data | |
NSDictionary *bData = [bodyDict objectForKey:bodyName]; | |
// create body object | |
BodyNode* bNode = [[BodyNode alloc] init]; | |
// add the body element to the cache | |
[_bodyNodes setObject:bNode forKey:bodyName]; | |
// set anchor point | |
bNode.anchorPoint = CGPointFromString([bData objectForKey:@"anchorpoint"]); | |
bNode.canRotate = [[bData objectForKey:@"canRotate"] boolValue]; | |
bNode.obeyGravity = [[bData objectForKey:@"obeyGravity"] boolValue]; | |
bNode.dynamicBody = [[bData objectForKey:@"physicsBodyTypeDynamic"] boolValue]; | |
CGSize bodySize = CGSizeFromString([bData objectForKey:@"size"]); | |
// iterate through the fixtures | |
NSArray* fixtureList = [bData objectForKey:@"fixtures"]; | |
for(NSDictionary *fData in fixtureList) { | |
// create fixture | |
FixtureNode* fNode = [[FixtureNode alloc] init]; | |
if(!fNode) { | |
return FALSE; | |
} | |
// add the fixture to the body | |
[bNode.fixtures addObject:fNode]; | |
// vitals for shape | |
fNode.friction = [[fData objectForKey:@"friction"] floatValue]; | |
fNode.elasticity = [[fData objectForKey:@"elasticity"] floatValue]; | |
fNode.mass = [[fData objectForKey:@"mass"] floatValue]; | |
fNode.surfaceVelocity = CGPointFromString([fData objectForKey:@"surface_velocity"]); | |
fNode.collisionGroup = [fData objectForKey:@"collision_group"]; | |
fNode.collisionType = [fData objectForKey:@"collision_type"]; | |
fNode.isSensor = [[fData objectForKey:@"isSensor"] boolValue]; | |
fNode.isPolylines = [[fData objectForKey:@"isPolylines"] boolValue]; | |
fNode.cornerRadius = [[fData objectForKey:@"cornerRadius"] intValue]; | |
NSString* fixtureType = [fData objectForKey:@"fixture_type"]; | |
// read polygon fixtures. One concave fixture may consist of several convex polygons | |
if([fixtureType isEqual:@"POLYGON"] && (!fNode.isPolylines)) { | |
fNode.fixtureType = POLYGON_FIXTURE; | |
fNode.area = 0.0f; | |
NSArray* polygonsArray = [fData objectForKey:@"polygons"]; | |
for(NSArray* polygonArray in polygonsArray) { | |
Polygon* poly = [[Polygon alloc] init]; | |
if(!poly) { | |
return FALSE; | |
} | |
// add the polygon to the fixture | |
[fNode.polygons addObject:poly]; | |
// size the pointer to the vertices | |
poly.numVertices = (int)[polygonArray count]; | |
CGPoint* vertices = poly.vertices = malloc(sizeof(CGPoint) * poly.numVertices); | |
if(!vertices) { | |
return FALSE; | |
} | |
// iterate through the strings in the array and extract points | |
int vindex = 0; | |
for(NSString *pointString in polygonArray) { | |
CGPoint offset = CGPointFromString(pointString); | |
vertices[vindex].x = offset.x / designScale + bodySize.width / designScale * bNode.anchorPoint.x; | |
vertices[vindex].y = offset.y / designScale + bodySize.height / designScale * bNode.anchorPoint.y; | |
vindex++; | |
} | |
poly.area = area(poly.vertices, poly.numVertices); | |
fNode.area += poly.area; | |
} | |
fNode.density = fNode.mass/fNode.area; | |
} else if([fixtureType isEqual:@"POLYGON"] && (fNode.isPolylines)) { | |
fNode.fixtureType = POLYLINE_FIXTURE; | |
NSArray* polyVerts = [fData objectForKey:@"hull"]; | |
// size the pointer to the vertices | |
int numVertices = (int)[polyVerts count]; | |
CGPoint* points = fNode.polyPoints = malloc(sizeof(CGPoint) * numVertices); | |
if(!fNode.polyPoints) { | |
return FALSE; | |
} | |
// iterate through the strings in the array and extract points | |
int vindex = 0; | |
for (NSString *pointString in polyVerts) { | |
CGPoint offset = CGPointFromString(pointString); | |
points[vindex].x = offset.x / designScale + bodySize.width / designScale * bNode.anchorPoint.x; | |
points[vindex].y = offset.y / designScale + bodySize.height / designScale * bNode.anchorPoint.y; | |
vindex++; | |
} | |
fNode.numPolyPoints = vindex; | |
} else if([fixtureType isEqual:@"CIRCLE"]) { | |
fNode.fixtureType = CIRCLE_FIXTURE; | |
NSDictionary* circleData = [fData objectForKey:@"circle"]; | |
CGPoint center = CGPointFromString([circleData objectForKey:@"position"]); | |
center.x += bodySize.width / designScale * bNode.anchorPoint.x; | |
center.y += bodySize.height / designScale * bNode.anchorPoint.y; | |
fNode.center = center; | |
fNode.radius = [[circleData objectForKey:@"radius"] floatValue] / designScale; | |
} else { | |
// unknown type | |
assert(0); | |
} | |
} | |
} | |
return TRUE; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment