I have 2 questions with b2Body:
What is the difference between b2Body and b2BodyDef?
How would I add a b2Body to a CCScene with coordinates from a CGRect which I already have coded? Also how would I add userData to it so I can keep a reference to this?
Thanks!
A b2BodyDef is used to define information about a body as a whole, such as position and rotation. Compared to the other information you require for a b2Body, such as friction and resititution, that is defined on a per fixture basis using b2Fixtures. The b2Body is an amalgamation of a body definition and at least one fixture.
With regard to creating the body from a predefined rect, I'd advise using setAsBox: assuming you are using a b2PolygonShape.
The way I usually accomplish the joining of the two is to create a class called BodyNode which has ivars of a b2Body and a CCSprite. Assign either the BodyNode, i.e. self or the sprite as the userData and update them as follows:
-(void) onEnter
{
[self scheduleUpdate];
[super onEnter];
}
-(void) update:(ccTime) dt
{
//Update the position of the sprite to the position of the body
//Update the rotation of the body to the rotation of the sprite. Take care to note that the rotation of the sprite is in degrees whereas the rotation of the body is in radians.
}
Related
I am having a physicsSprite of kinematics body type and I want to move the body and sprite bit down and back to its position. I tried it like this Inside update method:
for (NSValue *bodyValue in [self getGoalPostBodiesList])
{
b2Body *gPBody = (b2Body *)[bodyValue pointerValue];
PhysicsSprite *pSprite =(PhysicsSprite *) gPBody->GetUserData();
NSLog(#"\n tag value of Sprite = %d",pSprite.tag);
if(pSprite == goal1)
{
pSprite.position = CGPointMake((gPBody->GetPosition().x)*32.0,(gPBody->GetPosition().y)*32.0);
float angle = gPBody->GetAngle();
pSprite.rotation = -(CC_RADIANS_TO_DEGREES(angle));
id moveDownAction = [CCMoveTo actionWithDuration:0.5 position:CGPointMake(pSprite.position.x,(pSprite.position.y )- 40)];
id moveUpAction = [CCMoveTo actionWithDuration:0.5 position:CGPointMake(pSprite.position.x,(pSprite.position.y )+ 40)];
CCSequence *seqAction = [CCSequence actions:moveDownAction,moveUpAction, nil];
[pSprite runAction:seqAction];
b2Vec2 pos = b2Vec2(pSprite.position.x/32.0, pSprite.position.y/32.0);
gPBody->SetTransform(pos, CC_DEGREES_TO_RADIANS(pSprite.rotation));
gPBody->SetLinearVelocity(b2Vec2(0.0f, 0.0f));
gPBody->SetAngularVelocity(0.0f);
}
}
Still the sprite is not changing its position.
Anyone's help will be deeply appreciated.
Thanks all,
MONISH
To summarize your code, you update the position of your sprite to reflect that of the body, start an animation, and then update the position of the body to correspond to the position of the sprite. So naturally, nothing should move here, since your CCMoveTo actions have not exerted any effect on your sprite yet.
Second, your update method may be called very often, like dozens of times per second, so the animation gets reset continously and will not make any visible progress.
To follow a consistent pattern, how about you set the velocity of your kinematic bodies. Also, update the position of your sprites to correspond to these bodies as you would do for dynamic bodies, but don't set the transformation of your bodies to correspond to their sprites.
I'm trying to follow some sprite by some instance of CCParticleSystem using CCFollow. I don't want to make instance of CCParticleSystem a child of sprite, because I want it to be displayed some time after sprite removed.
When moving sprite from bottom-left to top-right corner my ParticleSystem moves from center to bottom-left corner. I can't understand why does it happen.
Here is sample code:
-(id) init
{
if( (self=[super init]) ) {
CCSprite *someSprite = [CCSprite spriteWithFile:#"Icon.png"];
[self addChild:someSprite];
id action = [CCMoveTo actionWithDuration:5 position:ccp(480,320)];
[someSprite runAction:action];
CCParticleSystemQuad *effect = [CCParticleMeteor node];
// effect.positionType = kCCPositionTypeFree;
// effect.positionType = kCCPositionTypeRelative;
// effect.positionType = kCCPositionTypeGrouped; changing of positionType to any of this options does not make any sense
[effect runAction:[CCFollow actionWithTarget:someSprite]];
[self addChild:effect];
}
return self;
}
It's a case of bad documentation.
I tried this myself and got the same result. By looking at the code and the fragment of documentation that exists, the CCFollow action is intended to be used only on fullscreen nodes like CCLayer, CCScene and perhaps CCNode. I would document it as follows:
CCFollow is an action for scrolling the entire screen by allowing the
layer or scene follow another node, such as a player object. It's an
alternative for moving the Camera object or updating the position
manually.
I have a standard cocos2d startup layer( HelloWorldLayer). I created another class of type CCNode named "Terrain" for my terrain. Then i add it to my layer in the layer's init:
terrain = [[Terrain alloc] initWithWorld:world AndLevel:0];
[self addChild:terrain z:1];
i add a 'CarObject' class (a CCSprite class), and add a car object to my terrain
car = [[CarObject alloc] initWithWorld:world];
[terrain addChild:car];
-i.e. in both the initWithWorld for terrain and car, i initialize some Box2d code
I then try to center my car object to my screen when i move it, i do this in my update method:
float offsetX = car.position.x;
float offsetY = car.position.y;
[terrain setOffsetX:(int)offsetX andOffsetY:(int)offsetY];
where the setOffsetX.. method is:
- (void) setOffsetX:(int)newOffsetX andOffsetY:(int)newOffsetY {
_offsetX = newOffsetX;
_offsetY = newOffsetY;
CGSize winSize = [CCDirector sharedDirector].winSize;
self.position = CGPointMake(-(_offsetX - winSize.width/2), -(_offsetY - winSize.height/2));
}
When i use a NSLog to see if the terrain position changes, i can see that the position actually chages, but the view does not. What am i doing wrong? am sure it's a dumb mistake!
btw, if i try this in my HelloWorldLayer's update method (instead of [terrain setOffsetX..])
self.position = CGPointMake(self.position.x-1, self.position.y);
the terrain is moving.
Car is a child of Terrain. Car's position is therefore relative to Terrain's position. Since you base Terrain's position on Car's position, which is actually relative to Terrain's position, you may be simply running into the effect that your position updates simply cancel each other out.
If you want to move the Terrain while keeping the Car centered, you shouldn't add the Car as a child of Terrain. Instead add it to the same node as the Terrain (HelloWorldLayer). Then you can move the Car and Terrain independently of each other.
I've been looking around for a while, and I have not been able to find an answer to this for some reason. It seems simple enough, but maybe I just can't find the right function in the library.
I have a scene with a layer that contains a bunch of CCNodes with each one CCSprite in them.
During the application, I move around the position of the main layer, so that I "pan" around a camera in a way. (i.e. I translate the entire layer so that the viewport changes).
Now I want to determine the absolute position of a CCNode in screen coordinates. The position property return the position relative to the parent node, but I really would like this transformed to its actual position on screen.
Also, as an added bonus, it would be awesome if I could express this position as coordinate system where 0,0 maps to the upper left of the screen, and 1,1 maps to the lower right of the screen. (So I stay compatible with all devices)
Edit: Note that the solution should work for any hierarchy of CCNodes preferably.
Every CCNode, and descendants thereof, has a method named convertToWorldSpace:(CGPoint)p
This returns the coordinates relative to your scene.
When you have this coordinate, flip your Y-axis, as you want 0,0 to be in the top left.
CCNode * myNode = [[CCNode alloc] init];
myNode.position = CGPointMake(150.0f, 100.0f);
CCSprite * mySprite = [[CCSprite alloc] init]; // CCSprite is a descendant of CCNode
[myNode addChild: mySprite];
mySprite.position = CGPointMake(-50.0f, 50.0f);
// we now have a node with a sprite in in. On this sprite (well, maybe
// it you should attack the node to a scene) you can call convertToWorldSpace:
CGPoint worldCoord = [mySprite convertToWorldSpace: mySprite.position];
// to convert it to Y0 = top we should take the Y of the screen, and subtract the worldCoord Y
worldCoord = CGPointMake(worldCoord.x, ((CGSize)[[CCDirector sharedDirector] displaySizeInPixels]).height - worldCoord.y);
// This is dry coded so may contain an error here or there.
// Let me know if this doesn't work.
[mySprite release];
[myNode release];
Help me out with some sample code for getting the position of a sprite in Box2d in iPhone.
To get the position of a sprite from a box2d bodyDef you would first want to save your sprite in the userData property of the bodyDef.
For example, in a factory method that creates a ball within the physics environment:
//create the body
b2BodyDef initBodyDef;
initBodyDef.type = b2_dynamicBody;
initBodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);
//Save the sprite in the userData property of the bodyDef, to access later
initBodyDef.userData = ballSprite;
b2Body *body = world->CreateBody(&initBodyDef);
//Rest of the factory method ............ (i.e. create shape, create fixture)
Then when you want to access the position of the sprite, for example when doing collision detection, you would get the pointer to the sprite within the userData property of the bodyDef:
This would be in the tick method (where collision detection takes place), or whereever you need to get the position of the sprite.
CCSprite *mySprite = (CCSprite *) bodyDef->GetUserData();
CGPoint spritePosition = mySprite.position;
In the first line of code above we create a sprite object and call the GetUserData method on our bodyDef, which returns the sprite we saved earlier. Note we have to cast the returned userData or it will return an error. Once we have the sprite saved all we have to do is call the position property on our mySprite pointer. Thats it.