1
votes

I am in the process of making a game and I need an object to move only when the buttons are pressed. I have a method that begins the movement, and so far I am ending the movement of an object by having destroying the body of the object. The problem that I have is that I can't move the object again since the program will now crash. I'm wondering if there is a way to recreate the body once its destroyed since my current method of checking if the body still exists isn't working.

Here is my code.

NSMutableArray *spaceObjectsArray;

#pragma mark - HelloWorldLayer

@interface HelloWorldLayer()
-(void) initPhysics;
-(void) addNewSpriteAtPosition:(CGPoint)p;
-(void) createMenu;
@end

@implementation HelloWorldLayer

+(CCScene *) scene
{
    // 'scene' is an autorelease object.
    CCScene *scene = [CCScene node];

    // 'layer' is an autorelease object.
    HelloWorldLayer *layer = [HelloWorldLayer node];

    // add layer as a child to scene
    [scene addChild: layer];

    // return the scene
    return scene;
}

-(void)gameLogic:(ccTime)delta
{
    [self addSpaceObjects];
}

-(void) addSpaceObjects
{

    _spaceObject = [CCSprite spriteWithFile:@"blueDot.jpg"];


    //create spaceObject body
    b2BodyDef spaceObjectbBodyDef;
    spaceObjectbBodyDef.type=b2_dynamicBody;
    spaceObjectbBodyDef.userData = _spaceObject;
    //make the location of spaceObject
    CGSize winSize = [CCDirector sharedDirector].winSize;
    int minX= _spaceObject.contentSize.width/2;
    int maxX = winSize.width - _spaceObject.contentSize.width/2;
    int rangeX = maxX - minX;
    int actualX = (arc4random() % rangeX) + minX;
    _spaceObject.position = ccp(actualX, winSize.height + _ship.contentSize.height);
    spaceObjectbBodyDef.position.Set(actualX/PTM_RATIO, (winSize.height+_ship.contentSize.height)/PTM_RATIO);
    _spaceObjectBody= _world->CreateBody(&spaceObjectbBodyDef);

    //create spaceObject shape
    b2PolygonShape spaceObjectShape;
    spaceObjectShape.SetAsBox(_spaceObject.contentSize.width/PTM_RATIO/2, _spaceObject.contentSize.height/PTM_RATIO/2);

    //create spaceObject fixture
    b2FixtureDef spaceObjectShapeDef;
    spaceObjectShapeDef.shape= &spaceObjectShape;
    spaceObjectShapeDef.density = 2;
    spaceObjectShapeDef.restitution =0;
    spaceObjectShapeDef.friction=0;
    _spaceObjectFixture = _spaceObjectBody->CreateFixture(&spaceObjectShapeDef);

    [self addChild:_spaceObject];
    _spaceObject.tag=1;
    [spaceObjectsArray addObject:_spaceObject];

   //aply force on the object
    int randomValue = ((arc4random() % 5) *-1);
    b2Vec2 force = b2Vec2(0,randomValue);
    _spaceObjectBody ->ApplyLinearImpulse(force, _spaceObjectBody->GetPosition());


}

init method that contains the body creations and definitions

-(id) init
{
    if( (self=[super init])) {

        CGSize s = [CCDirector sharedDirector].winSize;


        //create spaceShip sprite and add it to the layer
        _ship = [CCSprite spriteWithFile:@"theShip.gif" ];
        _ship.position = ccp(s.width/2, 1.25*_ship.contentSize.height);
        [self addChild:_ship];

        //create the world
        b2Vec2 gravity = b2Vec2_zero;
        _world = new b2World(gravity);

        //create ship body
        b2BodyDef shipBodyDef;
        shipBodyDef.type = b2_dynamicBody;
        shipBodyDef.position.Set((s.width/2)/PTM_RATIO, (1.25*_ship.contentSize.height)/PTM_RATIO);
        shipBodyDef.userData = _ship;

        if(_shipBody == NULL){
        _shipBody =_world->CreateBody(&shipBodyDef);
        }

        //create ship shape
        b2PolygonShape shipShape;
        shipShape.SetAsBox(_ship.contentSize.width/PTM_RATIO/2, _ship.contentSize.height/PTM_RATIO/2);

        //create Ship definition and add to body
        b2FixtureDef ShipShapeDef;
        ShipShapeDef.shape = &shipShape;
        ShipShapeDef.density = 3;
        ShipShapeDef.friction =0;
        ShipShapeDef.restitution =0;
        _shipFixture = _shipBody->CreateFixture(&ShipShapeDef);

        //make the paddles
        //bottom left one
        _paddle1 = [CCSprite spriteWithFile:@"spritePaddle.jpeg"];
        int bottomOfScreenX = 0 + _paddle1.contentSize.width/2;
        int bottomOfScreenY = 0+_paddle1.contentSize.height/2;
        _paddle1.position = ccp(bottomOfScreenX,bottomOfScreenY);
        [self addChild:_paddle1];
        //bottom right one
        _paddle2 = [CCSprite spriteWithFile:@"spritePaddle.jpeg"];
        int bottomRightOfScreenX = s.width - _paddle2.contentSize.width/2;
        _paddle2.position = ccp(bottomRightOfScreenX, bottomOfScreenY);
        [self addChild:_paddle2];


        //continuously spawn spaceObjects
        [self schedule:@selector(gameLogic:) interval:1];


        // enable events

        self.touchEnabled = YES;



        // init physics
        [self schedule:@selector(tick:)];


    }
    return self;
}

-(void)tick:(ccTime) delta
{//this method is to simulate physics and to test for the position of where objects should be if force has been applied to them
    _world->Step(delta, 8, 8);
    for (b2Body *b=_world->GetBodyList(); b; b=b->GetNext()){
        if (b->GetUserData() != NULL){
            CCSprite *shipData = (CCSprite *)b->GetUserData();
            shipData.position = ccp(b->GetPosition().x *PTM_RATIO, b->GetPosition().y *PTM_RATIO);
        }
    }
}

The paddles to move the ship are touched logic

-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    //Set up a way for touches to be turned into locations onscreen
    NSSet *allTouches = [event allTouches];
    UITouch *touch = [allTouches anyObject];
    CGPoint location = [touch locationInView:[touch view]];
    location = [[CCDirector sharedDirector] convertToGL:location ];

    //check to see if Left Paddle is being pressed
    if (CGRectContainsPoint([_paddle1 boundingBox], location)){
        b2Vec2 force = b2Vec2(-5,0);
        _shipBody->ApplyLinearImpulse(force, _shipBody ->GetPosition());
        }
    if (CGRectContainsPoint([_paddle2 boundingBox], location)){
        b2Vec2 force = b2Vec2(5,0);
        _shipBody->ApplyLinearImpulse(force, _shipBody->GetPosition());
    }
}

The paddle box is no longer being touched logic

-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    _world->DestroyBody(_shipBody);

}
-(void) dealloc
{
    delete _world;
    _world = NULL;



    [super dealloc];
}   

@end
1

1 Answers

0
votes

Destroying the body to end the movement of it is not the best solution. You should only remove the body if you really don't want it to be part of the simulation any more.

There are several options for stopping the body's movement:

1 - Set its linear velocity to 0. This will bring it to an immediate stop. If something else is pushing on it (e.g. contact wit body), you will have to decide what to do.

body->SetLinearVelocity(b2Vec2(0,0)));

2 - Set its linear/angular damping to 0. This will dissipate the momentum it has so it will slowly stop. The factor you use should be greater than 0. The body will come to a halt more quickly with larger values and it will be resistant to movement from other bodies (if they bump it, it will slow down and stop again). Remember to turn the linear/angular damping back to 0 when you want the body to start moving.

body->SetLinearDamping(0.2);

body->SetAngularDamping(0.2);

3 - Give it a target position to seek to and set the position as the place you want it to be. This is basically a feedback control loop where you are applying a force to get it to move towards where you want the body to stay. It can be used to make a body follow paths, etc. The code below is part of a larger code base (you can see it here), but you should be able to get the general idea. This function applies thrust to the object so that it pushes towards a target.

void MovingEntity::ApplyThrust()
{
   // Get the distance to the target.
   b2Vec2 toTarget = GetTargetPos() - GetBody()->GetWorldCenter();
   toTarget.Normalize();
   b2Vec2 desiredVel = GetMaxSpeed()*toTarget;
   b2Vec2 currentVel = GetBody()->GetLinearVelocity();
   b2Vec2 thrust = GetMaxLinearAcceleration()*(desiredVel - currentVel);
   GetBody()->ApplyForceToCenter(thrust);
}