@bradparks, thanks for the link. @eshirt, here is an outline. My game only has two player pieces, so I chose not to add an Actor class but instead connect my game pieces classes directly. Not that I know what I'm doing :) but the player pieces are spawning correctly.
Someone may see redundant code here, I'd certainly like to hear about that! This is my synthesis of tutorials, testbeds, and forum topics.
//Ball.h
#import <Foundation/Foundation.h>
#import "Box2D.h"
#import <cocos2d.h>
@class GameScene;
@interface Ball : Sprite {
@private
GameScene *game;
b2World *world;
b2Body *body;
b2BodyDef *bodyDef;
b2CircleShape *shapeDef;
b2FixtureDef *fixtureDef;
}
-(void) addBall;
-(id) initWithMgr:(CGPoint)points spritemgr:(AtlasSpriteManager *)mgr; // atlasManager reference
-(id) addAtlasSprite:(AtlasSprite *)pSprite;
@property (nonatomic, retain) GameScene *game;
@property (nonatomic, assign) b2Body *body;
@property (nonatomic, assign) b2BodyDef *bodyDef;
@property (nonatomic, assign) b2CircleShape *shapeDef;
@property (nonatomic, assign) b2FixtureDef *fixtureDef;
@end
Ball.m Implementation:
#import "Ball.h"
#import "GameScene.h"
#define PTM_RATIO 32
@implementation Ball
- (void)dealloc {
delete bodyDef;
delete shapeDef;
delete fixtureDef;
[self setBody:nil];
[self setBodyDef:nil];
[self setShapeDef:nil];
[self setFixtureDef:nil];
[super dealloc];
}
- (id)initWithMgr:(CGPoint)points spritemgr:(AtlasSpriteManager *)mgr {
if(self = [super init]) {
[self init];
[self setBodyDef:new b2BodyDef];
[self bodyDef]->position.Set(points.x/PTM_RATIO, points.y/PTM_RATIO);
[self setShapeDef:new b2CircleShape];
[self shapeDef]->m_radius = 12.0f/PTM_RATIO;
[self setFixtureDef:new b2FixtureDef];
[self fixtureDef]->shape = shapeDef;
[self fixtureDef]->friction = 0.01f;
[self fixtureDef]->density = 0.4f;
[self fixtureDef]->restitution = 0.8f;
}
return self;
}
-(id) addAtlasSprite:(AtlasSprite *)pSprite {
[self body]->SetUserData(pSprite);
return self;
}
- (void)addBall {
[self setBody:[[self game] world]->CreateBody([self bodyDef])];
[self body]->CreateFixture([self fixtureDef]);
}
@synthesize game;
@synthesize body;
@synthesize bodyDef;
@synthesize shapeDef;
@synthesize fixtureDef;
@end
And in the main game scene:
//GameScene.h
#import "cocos2d.h"
#import "Box2D.h"
#import "GLES-Render.h"
@class Ball;
@interface GameScene : Layer
{
@public
b2World* world;
b2Vec2 m_mouseWorld;
b2Vec2 m_mouseWorld2;
b2MouseJoint* m_mouseJoint;
b2MouseJoint* m_mouseJoint2;
b2Body* groundBody;
int32 m_pointCount;
GLESDebugDraw *m_debugDraw;
}
- (void)addBallToGame:(Ball *)aBall;
@property (nonatomic, readonly) b2World *world;
@property (nonatomic, readonly) b2Body *groundBody;
// returns a Scene that contains the HelloWorld as the only child
+(id) scene;
@end
GameScene Implementation (partial code highlights):
//GameScene.m
#import "Ball.h"
#import "GameScene.h"
#pragma mark Global Values
#define PTM_RATIO 32 //convert between Box2D units (m) and iphone (pixels)
enum { //for Atlas Manager
kTagTileMap = 1,
kTagSpriteManager = 1,
kTagAnimation1 = 1,
};
const int32 k_maxContactPoints = 2048; // for box 2d
void *mainLayer;
#pragma mark GameLayer
@implementation GameScene
#pragma mark dealloc routines
// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
delete world;
[self setWorld:nil];
//delete m_groundBody;
//[self groundBody:nil];
delete m_debugDraw;
[super dealloc];
}
#pragma mark Physics Accessors
@synthesize world;
@synthesize groundBody;
#pragma mark return Game Scene
//select scene
+(id) scene
{
// 'scene' is an autorelease object.
Scene *scene = [Scene node];
// 'layer' is an autorelease object.
GameScene *layer = [GameScene node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
// this starts the magic of adding the game piece
- (void)addBallToGame:(Ball *)aBall {
if(aBall) {
[aBall setGame:self];
[aBall addBall];
}
}
Then in the GameScene init I call the b2World, set groundbox as usual, then call the game pieces :
-(id) init
{
if( (self=[super init])) {
//...call debug, b2World, groundbox etc, then set up AtlasSprite and call Ball class:
#pragma mark AtlasManager setup
// Set up atlasManager
AtlasSpriteManager *mgr = [AtlasSpriteManager spriteManagerWithFile:@"spriteSheet.png" capacity:10];
[self addChild:mgr z:10 tag:kTagSpriteManager];
#pragma mark -- Add Pieces --
// Ball
Ball *anBall = [[[Ball alloc] initWithMgr:ccp(160, 240) spritemgr:mgr] autorelease];
[self addBallToGame:anBall];
AtlasSprite *BallSprite = [AtlasSprite spriteWithRect:CGRectMake(0,0,57,64) spriteManager:mgr];
[mgr addChild:BallSprite z:0];
[anBall addAtlasSprite:(AtlasSprite *)BallSprite];
// ... repeat for other gamepieces...
[self schedule: @selector(tick:)];
}
return self;
}
I hope this is helpful. No doubt it can be improved beyond my beginner's knowledge. If you find a way to sort out multitouch mousejoints I'd be grateful!