tracking a sprite body position as it moves - ios

This might sound quite straightforward. I want to keep track of a sprite body position only AFTER it has been moved by a mouseJoint so I can limit it's movement by comparing it's position (at any given time after mouseJoint is released) with a given position. Please help.
UPDATED
Here's what I did. I made a method that returns the sprite's position, which I called in the ccTouchesEnded method:
- (CGPoint)spritePositionRelease {
for(b2Body *b = mouseJoint->GetBodyB(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL)
{
CCSprite *mySprite = (CCSprite*)b->GetUserData();
if (mySprite.tag == 1) {
mySprite.position = CGPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO);
spritePosition = mySprite.position;
CCLOG(#"the sprite position is x:%0.2f , y:%0.2f", spritePosition.x, spritePosition.y);
return spritePosition;
}
}
}
}
ccTouchesEnded:
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (mouseJoint)
{
[self spritePositionRelease];
world->DestroyJoint(mouseJoint);
mouseJoint = NULL;
}
}
In the tick method I added the following code:
for(b2Body *b = world->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL)
{
CCSprite *mySprite = (CCSprite*)b->GetUserData();
if (mySprite.tag == 1) {
mySprite.position = CGPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO);
CGPoint spriteCurrentPosition = mySprite.position;
if ( spritePosition.x != spriteCurrentPosition.x &&
spritePosition.y == spriteCurrentPosition.y) {
CCLOG(#"the sprite limit for y is y:%0.2f has been reached", spriteCurrentPosition.y);
}
}
}
}
I initialized the spritePosition in the HelloWorldLayer.h class. I know I've done something wrong. I don't think the spritePosition I am accessing in the tick method has the same value as the spritePosition in the ccTouchesEnded method, hence the condition in the tick method never gets satisfied. I am not sure how to get this corrected. Please help

You can use ccpdistance(X2 , X1); to find the distance between two points. And from that distance you can limit the range that sprite can move.

Related

Box2d object throwing smoother and on same velocity

I have an box2d object which i am throwing from top to bottom and i have set its speed constant but when i run it , that object has different speed sometimes and how can i make this object more smoother.
Following are some methods to show how i have created box2d world and box2d body object.
#pragma -mark Box2D World
-(void)createWorld
{
// Define the gravity vector.
b2Vec2 b_gravity;
b_gravity.Set(0.0f, -9.8f);
// 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(b_gravity);
world->SetAllowSleeping(doSleep);
world->SetContinuousPhysics(true);
}
-(void) createWeb
{
freeBodySprite = [CCSprite spriteWithFile:#"web1.png"];//web_ani_6_1
//freeBodySprite.position = ccp(100, 300);
[self addChild:freeBodySprite z:2 tag:6];
CGPoint startPos = CGPointMake(100, 320/1.25);
bodyDef.type = b2_staticBody;
bodyDef.position = [self toMeters:startPos];
bodyDef.userData = freeBodySprite;
float radiusInMeters = ((freeBodySprite.contentSize.width * freeBodySprite.scale/PTM_RATIO) * 0.5f);
shape.m_radius = radiusInMeters;
fixtureDef.shape = &shape;
fixtureDef.density = 0.07f;
fixtureDef.friction = 0.1f;
fixtureDef.restitution = 0.1f;
circularObstacleBody = world->CreateBody(&bodyDef);
stoneFixture = circularObstacleBody->CreateFixture(&fixtureDef);
freeBody = circularObstacleBody;
}
-(b2Vec2) toMeters:(CGPoint)point
{
return b2Vec2(point.x / PTM_RATIO, point.y / PTM_RATIO);
}
-(b2Body *) getBodyAtLocation:(b2Vec2) aLocation {
for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
{
b2Fixture* bodyFixture = b->GetFixtureList();
if (bodyFixture->TestPoint(aLocation)){
return b;
}
}
return NULL;
}
-(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 = 3;
// 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());
}
}
}
This is my touch Event where i am getting angle and speed to throw .
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
//get the location of the end point of the swipe
UITouch *myTouch = [touches anyObject];
CGPoint location = [myTouch locationInView:[myTouch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
//CCLOG(#"Start -> %0.f || End -> %0.f",startPoint.x,location.x);
if (freeBody) {
//[self calcAngleAndRotateObjectStartingAtPoint:startPoint endingAtPoint:location];
self.isTouchEnabled = NO;
freeBody->SetType(b2_dynamicBody);
//this is the maximum force that can be applied
const CGFloat maxForce = 20;
//get the rotation b/w the start point and the end point
CGFloat rotAngle = atan2f(location.y - startPoint.y,location.x - startPoint.x);
//the distance of the swipe if the force
CGFloat distance = ccpDistance(startPoint, location) * 0.5;
//if (distance>maxForce)
distance = maxForce;
//else
// distance = 10;
//apply force
freeBody->ApplyForce(b2Vec2(cosf(rotAngle) * distance, sinf(rotAngle) * distance), freeBody->GetPosition());
//lose the weak reference to the body for next time usage.
freeBody = nil;
}
}
This is code i am using to throw , but sometimes its speed is faster and some time slower , and i have set maxForce = 20 for constant speed.
As the comment above world->Step() dictates, you should use fixed dt. Verify that dt is fixed and world->Step() is being called at regular interval.
Finnally i have solved this problem. i changed ApplyForce with SetLinearVelocity..
here is the code.
float spd = 10;
b2Vec2 velocity = spd*b2Vec2(cos(rotAngle), sin(rotAngle));
freeBody->SetLinearVelocity(velocity);

How to continuously check collisions in Tiled tilemaps during a CCMoveTo

Hello good people of stack overflow
I stand before you today with a (not very great) cocos2d issue.
How can I check collisions WHILE in an action, instead of checking the target tile?
I am working with a tilemap that has a meta layer where there are collidable tiles. The following code works when I click the collidable tile, but not when I click beyond it, if that happens he will just walk straight through.
Currently, I detect collisions with this code:
-(void)setPlayerPosition:(CGPoint)position {
CGPoint tileCoord = [self tileCoordForPosition:position];
int tileGid = [_meta tileGIDAt:tileCoord];
if (tileGid) {
NSDictionary *properties = [_tileMap propertiesForGID:tileGid];
if (properties) {
NSString *collision = properties[#"Collidable"];
if (collision && [collision isEqualToString:#"True"]) {
return;
}
}
}
float speed = 10;
CGPoint currentPlayerPos = _player.position;
CGPoint newPlayerPos = position;
double PlayerPosDifference = ccpDistance(currentPlayerPos, newPlayerPos) / 24;
int timeToMoveToNewPostition = PlayerPosDifference / speed;
id moveTo = [CCMoveTo actionWithDuration:timeToMoveToNewPostition position:position];
[_player runAction:moveTo];
//_player.position = position;
}
By the way, this gets called from this method:
-(void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
[_player stopAllActions];
CGPoint touchLocation = [touch locationInView:touch.view];
touchLocation = [[CCDirector sharedDirector] convertToGL:touchLocation];
touchLocation = [self convertToNodeSpace:touchLocation];
CGPoint playerPos = _player.position;
CGPoint diff = ccpSub(touchLocation, playerPos);
int diffx = diff.x;
int diffy = diff.y;
int adiffx = diffx / 24;
int adiffy = diffy / 24; //Porque my tile size is 24
if ( abs(diff.x) > abs(diff.y) ) {
if (diff.x > 0) {
playerPos.x = playerPos.x + adiffx * 24;
} else {
playerPos.x = playerPos.x + adiffx * 24;
}
} else {
if (diff.y > 0) {
playerPos.y = playerPos.y + adiffy * 24;
}else{
playerPos.y = playerPos.y + adiffy * 24;
}
}
[self setPlayerPosition:playerPos];
}
I tried
[self schedule:#selector(setPlayerPosition:) interval:0.5];
Without any luck, that just instantly crashed the app at this
NSAssert( pos.x < _layerSize.width && pos.y < _layerSize.height && pos.x >=0 && pos.y >=0, #"TMXLayer: invalid position");
And i can't really blame it for crashing there.
How do I constantly check for collisions with the collidable meta tiles while a CCMoveTo is running?
In your init, schedule a collision detecting method:
[self schedule:#selector(checkCollisions:)];
Then define it as follows:
-(void)checkCollisions:(ccTime)dt
{
CGPoint currentPlayerPos = _player.position;
CGPoint tileCoord = [self tileCoordForPosition:currentPlayerPos];
int tileGid = [_meta tileGIDAt:tileCoord];
if (tileGid)
{
NSDictionary *properties = [_tileMap propertiesForGID:tileGid];
if (properties)
{
NSString *collision = properties[#"Collidable"];
if (collision && [collision isEqualToString:#"True"])
{
[_player stopAllActions];
return;
}
}
}
}
I using a similar meta layer. I personally skipped using MoveTo, and created my own update code that both moves the sprite and does collision detection.
I figure out what the potential new position would be, then I find out what tile position is in all four corners of the sprite at that new position, then I cycle through all tiles that the sprite would be on and if any are collidable, I stop the sprite.
I actually go a step further and check if moving only the x, or only the y changes the results, then move to that position instead if it does.

destroying a weldjoint when a sprite reaches a specific position

I have created a weldJoint between two sprites in my code (see update method below) and I also created a method that returns the exact position of a sprite when a mouseJoint is released. I want to compare the current position of the sprite to the spritePositionRelease value and destroy the weldJoint if the y values are the same and x values are different. Please help.
spritePositionRelease:
- (CGPoint)spritePositionRelease {
for(b2Body *b = mouseJoint->GetBodyB(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL)
{
CCSprite *mySprite = (CCSprite*)b->GetUserData();
if (mySprite.tag == 1) {
mySprite.position = CGPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO);
CGPoint spritePosition = mySprite.position;
CCLOG(#"the sprite position is x:%0.2f , y:%0.2f", spritePosition.x, spritePosition.y);
return spritePosition;
}
}
}
}
ccTouchesEnded:
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (mouseJoint)
{
[self spritePositionRelease];
world->DestroyJoint(mouseJoint);
mouseJoint = NULL;
}
}
update:
-(void) update: (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);
// using the iterator pos over the set
std::set<BodyPair *>::iterator pos;
for(pos = bodiesForJoints.begin(); pos != bodiesForJoints.end(); ++pos)
{
b2WeldJoint *weldJoint;
b2WeldJointDef weldJointDef;
BodyPair *bodyPair = *pos;
b2Body *bodyA = bodyPair->bodyA;
b2Body *bodyB = bodyPair->bodyB;
weldJointDef.Initialize(bodyA,
bodyB,
bodyA->GetWorldCenter());
weldJointDef.collideConnected = false;
weldJoint = (b2WeldJoint*) world->CreateJoint(&weldJointDef);
// Free the structure we allocated earlier.
free(bodyPair);
// Remove the entry from the set.
bodiesForJoints.erase(pos);
}
}
Are you sure that mouseJoint->GetBodyB() will always return the right body? Maybe you should check mouseJoint->GetBodyA() to?
Anyway your check would be quite simple:
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (mouseJoint)
{
CGPoint releasePoint = [self spritePositionRelease];
CGPoint touchPoint = [[touches anyObject] locationInView:[[touches anyObject] view]];
if((releasePoint.y==touchPoint.y) &&(releasePoint.x!=touchPoint.x))
{
//Destroy weld joint
}
world->DestroyJoint(mouseJoint);
mouseJoint = NULL;
}
}

sprite not updating to the correct position

I have written a code to display using CCLog the exact position of a sprite when a mousejoint moving it is released. Below is the Sprite.mm class and the ccTouchesEnded method (which is in the HelloWorldLayer.mm class). The sprite position is not updating, the output is constantly x: 0.00 and y: 0.00.
Sprite.mm:
-(id)addSprite:(CCLayer *)parentLayer
inWorld:(b2World *)world
{
PhysicsSprite *aSprite = [PhysicsSprite spriteWithFile:#"spriteIm.png"];
aSprite.tag = 1;
[parentLayer addChild:aSprite];
b2BodyDef spriteBodyDef;
spriteBodyDef.userData = aSprite;
spriteBodyDef.type = b2_dynamicBody;
CGSize s = [CCDirector sharedDirector].winSize;
spriteBodyDef.position = [Convert toMeters:ccp(s.width * 0.25,s.height-400)];
b2FixtureDef fixtureDef;
fixtureDef.density = 0.01;
b2CircleShape circleShape;
circleShape.m_radius = aSprite.contentSize.width/2 / PTM_RATIO;
fixtureDef.shape = &circleShape;
spriteBody = world->CreateBody( &spriteBodyDef );
spriteFixture = spriteBody->CreateFixture( &fixtureDef );
[aSprite setPhysicsBody:spriteBody];
return aSprite;
}
ccTouchesEnded:
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (mouseJoint)
{
for(b2Body *b = world->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL) {
CCSprite *mySprite = (CCSprite *)b->GetUserData();
if (mySprite.tag == 1) {
CGPoint spritePosition = mySprite.position;
CCLOG(#"the sprite position is x:%0.2f, y:%0.2f", spritePosition.x, spritePosition.y);
}
}
}
world->DestroyJoint(mouseJoint);
mouseJoint = NULL;
}
}
Please help. I've been at it for a few days.
I finally managed to get it working. I guess the x:0.00 and y:0.00 were because I was taking the position of the sprite not the body. The sprite is parented to the body so it was giving its position inside it's parent, which is 0,0. This is how I understand it. Here are the changes I made to the ccTouchesEnded code.
ccTouchesEnded:
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (mouseJoint)
{
for(b2Body *b = mouseJoint->GetBodyB(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL)
{
CCSprite *mySprite = (CCSprite*)b->GetUserData();
if (mySprite.tag == 1) {
mySprite.position = CGPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO);
CCLOG(#"the sprite postion is x:%0.2f , y:%0.2f", mySprite.position.x, mySprite.position.y);
}
}
}
world->DestroyJoint(mouseJoint);
mouseJoint = NULL;
}
}

Update CCSprite postion when b2Body moves?

In my Box2d game I found out that I must update my CCSprite position in the game loop so it works properly. I have looked through the Box2D docs to see what APIs there are to get the b2Body coordinates but I didn't see anything and this seems unclear.
Should I be moving each CCSprite manually or is there an easy way to move all CCSprite on screen to the b2Bodys associated with them?
Does anyone have any idea on how to do this?
Thanks!
Edit1:
Would this code just update every sprite in my world to the b2Body corresponding to them? And this will work with the x and y axis and rotation correct?
for(b2Body *b = _world->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL) {
CCSprite *sprite = (CCSprite *)b->GetUserData();
b2Vec2 b2Position = b2Vec2(sprite.position.x/PTM_RATIO,
sprite.position.y/PTM_RATIO);
float32 b2Angle = -1 * CC_DEGREES_TO_RADIANS(sprite.rotation);
b->SetTransform(b2Position, b2Angle);
}
}
simply do this:
In your init class schedule the tick method like this
[self schedule: #selector(tick:)];
-(void)tick: (ccTime) dt
for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetUserData() != NULL)
{
CCSprite *spr= (CCSprite*)b->GetUserData();
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());
}
}

Resources