Thanks a lot! This is a really good implementation, and the examples are really clear cut for all the uses it can have. :P
I'm having a little trouble implementing it, though. I can get the cells to show up and scroll, and show the images that I tell them to. Everything is fine there. Basically I've just been hacking at the default box2d example, and added this TableView to it on another layer, so that I could spawn an object based on what index was pressed. But the table view isn't reporting the table cell that was touched (and the CCLOG comment is still there, so it should be). The touch just goes down and is still handled by the base layer.
I essentially copied the exact code of your TableViewScene and changed the size of the cells.
'#import "ObjectTableView.h"
#import "objectCell.h"
#import "CCTableViewCell.h"
@implementation ObjectTableView
-(id)init {
if((self = [super init])){
NSAutoreleasePool *pool = [NSAutoreleasePool new];
CGSize winSize = [[CCDirector sharedDirector] winSize];
CGSize cSize = CGSizeMake(64,64);
buildObjects = [[CCTableView tableViewWithDataSource:self size:CGSizeMake(winSize.width, cSize.height)] retain];
buildObjects.direction = CCScrollViewDirectionHorizontal;
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"sprites.plist"];
buildObjects.position = ccp(0.0,0.0);
[self addChild:buildObjects z:1];
[buildObjects reloadData];
[pool drain];
}
return self;
}
#pragma mark -
#pragma mark TableView Delegate
-(void)table:(CCTableView *)table cellTouched:(CCTableViewCell *)cell {
CCLOG(@"cell touched at index: %i", cell.idx);
}
#pragma mark -
#pragma mark TableView DataSource
-(Class)cellClassForTable:(CCTableView *)table {
return [objectCell class];
}
-(CCTableViewCell *)table:(CCTableView *)table cellAtIndex:(NSUInteger)idx {
CCTableViewCell *cell;
NSString *spriteName;
CCSprite *sprite;
idx = idx%5;
cell = [table dequeueCell];
spriteName = [NSString stringWithFormat:@"cell%i.png", idx%10];
sprite = [CCSprite spriteWithSpriteFrameName:spriteName];
if (!cell) {
cell = [[objectCell new] autorelease];
}
cell.node = sprite;
return cell;
}
-(NSUInteger)numberOfCellsInTableView:(CCTableView *)table {
return 100;
}
-(void) dealloc {
[buildObjects release];
[super dealloc];
[[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames];
}
@end'
And then I added it to the box2D scene in init like this
#import "towerGameScene.h"
#import "ObjectTableView.h"
//Pixel to metres ratio. Box2D uses metres as the unit for measurement.
//This ratio defines how many pixels correspond to 1 Box2D "metre"
//Box2D is optimized for objects of 1x1 metre therefore it makes sense
//to define the ratio so that your most common object type is 1x1 metre.
#define PTM_RATIO 32
// enums that will be used as tags
enum {
kTagTileMap = 1,
kTagSpriteSheet = 1,
kTagAnimation1 = 1,
};
// HelloWorld implementation
@implementation towerGame
+(id) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
towerGame *layer = [towerGame node];
// add layer as a child to scene
[scene addChild: layer z:1];
// return the scene
return scene;
}
// initialize your instance here
-(id) init
{
if( (self=[super init])) {
// enable touches
self.isTouchEnabled = YES;
// enable accelerometer
self.isAccelerometerEnabled = YES;
CGSize screenSize = [CCDirector sharedDirector].winSize;
CCLOG(@"Screen width %0.2f screen height %0.2f",screenSize.width,screenSize.height);
CCSprite *bgLayer = [CCSprite spriteWithFile:@"background.png"];
bgLayer.position = ccp(160, 240);
[self addChild:bgLayer z:0];
CCSprite *drawerLayer = [CCSprite spriteWithFile:@"drawerBack.png"];
drawerLayer.position = ccp(screenSize.width/2, 32);
[self addChild:drawerLayer z:2];
[self addChild:[ObjectTableView node] z:3];
// Define the gravity vector.
b2Vec2 gravity;
gravity.Set(0.0f, -10.0f);
// Do we want to let bodies sleep?
// This will speed up the physics simulation
bool doSleep = true;
// Construct a world object, which will hold and simulate the rigid bodies.
world = new b2World(gravity, doSleep);
world->SetContinuousPhysics(true);
// Debug Draw functions
// m_debugDraw = new GLESDebugDraw( PTM_RATIO );
// world->SetDebugDraw(m_debugDraw);
// uint32 flags = 0;
// flags += b2DebugDraw::e_shapeBit;
// flags += b2DebugDraw::e_jointBit;
// flags += b2DebugDraw::e_aabbBit;
// flags += b2DebugDraw::e_pairBit;
// flags += b2DebugDraw::e_centerOfMassBit;
// m_debugDraw->SetFlags(flags);
// Define the ground body.
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0, 0); // bottom-left corner
// Call the body factory which allocates memory for the ground body
// from a pool and creates the ground box shape (also from a pool).
// The body is also added to the world.
b2Body* groundBody = world->CreateBody(&groundBodyDef);
// Define the ground box shape.
b2PolygonShape groundBox;
// bottom
groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(screenSize.width/PTM_RATIO,0));
groundBody->CreateFixture(&groundBox,0);
// top
groundBox.SetAsEdge(b2Vec2(0,screenSize.height/PTM_RATIO), b2Vec2(screenSize.width/PTM_RATIO,screenSize.height/PTM_RATIO));
groundBody->CreateFixture(&groundBox,0);
// left
groundBox.SetAsEdge(b2Vec2(0,screenSize.height/PTM_RATIO), b2Vec2(0,0));
groundBody->CreateFixture(&groundBox,0);
// right
groundBox.SetAsEdge(b2Vec2(screenSize.width/PTM_RATIO,screenSize.height/PTM_RATIO), b2Vec2(screenSize.width/PTM_RATIO,0));
groundBody->CreateFixture(&groundBox,0);
//Set up sprite
CCSpriteSheet *sheet = [CCSpriteSheet spriteSheetWithFile:@"sprites.png" capacity:8];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"sprites.plist"];
[self addChild:sheet z:0 tag:kTagSpriteSheet];
[self addNewSpriteWithCoords:ccp(screenSize.width/2, screenSize.height/2)];
CCLabel *label = [CCLabel labelWithString:@"Tap screen" fontName:@"Marker Felt" fontSize:32];
[self addChild:label z:0];
[label setColor:ccc3(0,0,255)];
label.position = ccp( screenSize.width/2, screenSize.height-50);
[self schedule: @selector(tick:)];
}
return self;
}
-(void) draw
{
// Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
// Needed states: GL_VERTEX_ARRAY,
// Unneeded states: GL_TEXTURE_2D, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
glDisable(GL_TEXTURE_2D);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
world->DrawDebugData();
// restore default GL states
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
}
-(void) addNewSpriteWithCoords:(CGPoint)p
{
CCLOG(@"Add sprite %0.2f x %02.f",p.x,p.y);
CCSpriteSheet *sheet = (CCSpriteSheet*) [self getChildByTag:kTagSpriteSheet];
b2PolygonShape dynamicBox;
b2CircleShape circle;
BOOL isCircle = FALSE;
b2FixtureDef fixtureDef;
int randInt = arc4random() % 5;
CCLOG(@"randInt = %d",randInt);
//TODO: Make this work as a switch without the b2Vec2 initialization error.
CCSprite *sprite;
if(randInt == 0){
//Object initialization
}
else if(randInt == 1){
//Object initialization
}
else if(randInt == 2){
//Object initialization
}
else if(randInt == 3){
//Object initialization
}
else{
//Object initialization
}
// Define the dynamic body fixture.
if(isCircle){
fixtureDef.shape = &circle;
}
else {
fixtureDef.shape = &dynamicBox;
}
[sheet addChild:sprite];
sprite.position = ccp( p.x, p.y);
// Define the dynamic body.
//Set up a 1m squared box in the physics world
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);
bodyDef.userData = sprite;
b2Body *body = world->CreateBody(&bodyDef);
body->CreateFixture(&fixtureDef);
}
-(void) tick: (ccTime) dt
{
//It is recommended that a fixed time step is used with Box2D for stability
//of the simulation, however, we are using a variable time step here.
//You need to make an informed choice, the following URL is useful
//http://gafferongames.com/game-physics/fix-your-timestep/
int32 velocityIterations = 8;
int32 positionIterations = 1;
// Instruct the world to perform a single step of simulation. It is
// generally best to keep the time step and iterations fixed.
world->Step(dt, velocityIterations, positionIterations);
//Iterate over the bodies in the physics world
for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetUserData() != NULL) {
//Synchronize the AtlasSprites position and rotation with the corresponding body
CCSprite *myActor = (CCSprite*)b->GetUserData();
myActor.position = CGPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO);
myActor.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
}
}
}
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
//Add a new body/atlas sprite at the touched location
/*for( UITouch *touch in touches ) {
CGPoint location = [touch locationInView: [touch view]];
location = [[CCDirector sharedDirector] convertToGL: location];
[self addNewSpriteWithCoords: location];
}*/
}
- (void)accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration
{
static float prevX=0, prevY=0;
//#define kFilterFactor 0.05f
#define kFilterFactor 1.0f // don't use filter. the code is here just as an example
float accelX = (float) acceleration.x * kFilterFactor + (1- kFilterFactor)*prevX;
float accelY = (float) acceleration.y * kFilterFactor + (1- kFilterFactor)*prevY;
prevX = accelX;
prevY = accelY;
// accelerometer values are in "Portrait" mode. Change them to Landscape left
// multiply the gravity by 10
b2Vec2 gravity( -accelY * 10, accelX * 10);
world->SetGravity( gravity );
}
// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
// in case you have something to dealloc, do it in this method
delete world;
world = NULL;
delete m_debugDraw;
// don't forget to call "super dealloc"
[super dealloc];
}
@end
With a background behind the TableView so that there's visual separation.
It's probably something simple that I'm missing, but if you have any insight as to what I might not be doing I would greatly appreciate it. :D
Thanks,
Jinno