Alright... I've simplified this as much as I know how to, but I'm still seeing the same behavior. I usually try to avoid posting large chunks of code and asking others to review them for me, but I'm at a loss right now as to where the issue is. I feel confident that there is something I'm not understanding correctly that is being manifested in this problem.
I've created a new sample project to debug this. The project simply has gravity and a ball that starts at the top of the screen. When the level loads, the ball falls and bounces. There is a reset menu on the screen. When I press reset, the ball the returns to the top of the screen but no longer falls. As before, after the reset, the tick function is called once and recognizes that there is one body, but never gets called again. The screen appears frozen at that point, but the menu is still responsive.
I've posted this simplified xcode project online if anyone wants to take a look at it: http://johntmcintosh.com/download/BallDrop_LevelReset.zip
Here's my simplified code and structure:
3 relevant classes: AppDelegate, HelloWorldScene, HelloWorldLayer
AppDelegate:
- (void) applicationDidFinishLaunching:(UIApplication*)application{
...
self.mainScene = [HelloWorldScene scene];
[_mainScene.layer reset];
[[CCDirector sharedDirector] runWithScene: _mainScene];
}
- (void)resetLevel {
[_mainScene.layer reset];
[[CCDirector sharedDirector] replaceScene:_mainScene];
}
HelloWorldScene:
@implementation HelloWorldScene
@synthesize layer = _layer;
+(id) scene
{
// 'scene' is an autorelease object.
HelloWorldScene *scene = [HelloWorldScene node];
// 'layer' is an autorelease object.
HelloWorldLayer *layer = [HelloWorldLayer node];
scene.layer = layer;
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
- (void)dealloc {
self.layer = nil;
[super dealloc];
}
@end
HelloWorldLayer:
@implementation HelloWorldLayer
-(void)reset
{
NSLog(@"reset called");
//-----------------------------------------
// Remove things from the world
//-----------------------------------------
// Remove the sprites and instance variables to nil
[self removeChild:_ball cleanup:YES];
_ball = nil;
// Remove bodies and set instance variables to nil
if(_ballBody!=nil){
_world->DestroyBody(_ballBody);
_ballBody = nil;
}
if(_groundBody!=nil){
_world->DestroyBody(_groundBody);
_groundBody = nil;
}
// Delete the world and set its variable to nil
delete _world;
_world = nil;
//-----------------------------------------
// Start adding things back to the world
//-----------------------------------------
CGSize winSize = [CCDirector sharedDirector].winSize;
int ballX = 200;
int ballY = 250;
// Create sprite and add it to the layer
_ball = [CCSprite spriteWithFile:@"fur2.png" rect:CGRectMake(0, 0, 60, 60)];
_ball.position = ccp(ballX, ballY);
[self addChild:_ball];
// Create a world
b2Vec2 gravity = b2Vec2(0.0f, -10.0);
bool doSleep = true;
_world = new b2World(gravity, doSleep);
// Create edges around the entire screen
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0,0);
_groundBody = _world->CreateBody(&groundBodyDef);
b2PolygonShape groundBox;
b2FixtureDef groundBoxDef;
groundBoxDef.shape = &groundBox;
groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(winSize.width/PTM_RATIO, 0));
_bottomFixture = _groundBody->CreateFixture(&groundBoxDef);
groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(0, winSize.height/PTM_RATIO));
_groundBody->CreateFixture(&groundBoxDef);
groundBox.SetAsEdge(b2Vec2(0, winSize.height/PTM_RATIO), b2Vec2(winSize.width/PTM_RATIO, winSize.height/PTM_RATIO));
_groundBody->CreateFixture(&groundBoxDef);
groundBox.SetAsEdge(b2Vec2(winSize.width/PTM_RATIO, winSize.height/PTM_RATIO), b2Vec2(winSize.width/PTM_RATIO, 0));
_groundBody->CreateFixture(&groundBoxDef);
// Create ball body and shape
b2BodyDef ballBodyDef;
ballBodyDef.type = b2_dynamicBody;
ballBodyDef.position.Set(ballX/PTM_RATIO, ballY/PTM_RATIO);
ballBodyDef.userData = _ball;
_ballBody = _world->CreateBody(&ballBodyDef);
// Create the ball's shape/fixture
b2CircleShape circle;
circle.m_radius = 30.0/PTM_RATIO;
b2FixtureDef ballShapeDef;
ballShapeDef.shape = &circle;
ballShapeDef.density = 1.0f;
ballShapeDef.friction = 0.2f;
ballShapeDef.restitution = 0.8f;
_ballFixture = _ballBody->CreateFixture(&ballShapeDef);
// Schedule the tick function to run regularly
[self schedule:@selector(tick:)];
NSLog(@"reset finished");
}
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init])) {
CGSize winSize = [CCDirector sharedDirector].winSize;
self.isTouchEnabled = YES;
// Create a restart button
CCLabelBMFont *menuLabel = [CCLabelBMFont labelWithString:@"Restart" fntFile:@"Arial.fnt"];
CCMenuItemLabel *menuItem = [CCMenuItemLabel itemWithLabel:menuLabel target:self selector:@selector(restartButtonTapped:)];
menuItem.position = ccp(240, 260);
CCMenu *menu = [CCMenu menuWithItems:menuItem, nil];
menu.position = CGPointZero;
[self addChild:menu];
}
return self;
}
- (void)tick:(ccTime) dt {
_world->Step(dt, 10, 10);
for(b2Body *b = _world->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL) {
CCSprite *ballData = (CCSprite *)b->GetUserData();
ballData.position = ccp(b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO);
ballData.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
}
}
}
// 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;
_ballBody = NULL;
_world = NULL;
// don't forget to call "super dealloc"
[super dealloc];
}
-(void)restartButtonTapped:(id)sender{
AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
[delegate resetLevel];
}
@end