Is object remain fixed when scrolling background in cocos2d - cocos2d-iphone

I have one question when infinite background scrolling is done, is the object remain fixed(like doodle in doodle jump, papy in papi jump) or these object really moves.Is only background move or both (background and object )move.plz someone help me.I am searching for this solution for 4/5 days,but can't get the solution.So plz someone help me. And if object does not move how to create such a illusion of object moving.

If you add the object to the same layer as the scrolling background, then it will scroll as the background scrolls.
If your looking for an effect like the hero in doodle jump, you may want to look at having two or more layers in a scene.
Layer 1: Scrolling Background Layer
Layer 2: Sprite layer
SomeScene.m
CCLayer *backgroundLayer = [[CCLayer alloc] init];
CCLayer *spriteLayer= [[CCLayer alloc] init];
[self addChild:backgroundLayer z:0];
[self addChild:spriteLayer z:1];
//Hero stays in one spot regardless of background scrolling.
CCSprite *squidHero = [[CCSprite alloc] initWithFile:#"squid.png"];
[spriteLayer addChild:squidHero];
If you want objects to scroll with the background add it to the background layer:
//Platform moves with background.
CCSprite *bouncePlatform= [[CCSprite alloc] initWithFile:#"bouncePlatform.png"];
[backgroundLayer addChild:bouncePlatform];

Another alternative is to use a CCFollow action. You would code as if the background is static (which it will be) and the player is moving (which it will be), but add a CCFollow action to the player. This essentially moves the camera so that it tracks your player.
You can also modify the classes so that you can get the CCFollow action to follow with an offset (i.e., so the player is not in the middle of the screen) as well as to have a smoothing effect to it, so that when the player moves, the follow action is not jerky. See the below code:
*NOTE I am using cocos2d-x, the c++ port. The methods are similar in cocos2d, and you should be able to modify these to fit the cocos2d syntax. Or search around -- I found these for cocos2d and then ported to c++.
//defines the action to constantly follow the player (in my case, "runner.p_sprite is the sprite pointing to the player)
FollowWithOffset* followAction = FollowWithOffset::create(runner.p_sprite, CCRectZero);
runAction(followAction);
And separately, I have copied the class definition for CCFollow to create my own class, CCFollowWithAction. This also has a smoothing effect (you can look this up more online) so that when the player moves, the actions are not jerky. I modified "initWithTarget," to take into account an offset, and "step," to add a smoothing action. You can see the modifications in the comments below.
bool FollowWithOffset::initWithTarget(CCNode *pFollowedNode, const CCRect& rect/* = CCRectZero*/)
{
CCAssert(pFollowedNode != NULL, "");
pFollowedNode->retain();
m_pobFollowedNode = pFollowedNode;
if (rect.equals(CCRectZero))
{
m_bBoundarySet = false;
}
else
{
m_bBoundarySet = true;
}
m_bBoundaryFullyCovered = false;
CCSize winSize = CCDirector::sharedDirector()->getWinSize();
m_obFullScreenSize = CCPointMake(winSize.width, winSize.height);
//m_obHalfScreenSize = ccpMult(m_obFullScreenSize, 0.5f);
m_obHalfScreenSize = CCPointMake(m_obFullScreenSize.x/2 + RUNNER_FOLLOW_OFFSET_X,
m_obFullScreenSize.y/2 + RUNNER_FOLLOW_OFFSET_Y);
if (m_bBoundarySet)
{
m_fLeftBoundary = -((rect.origin.x+rect.size.width) - m_obFullScreenSize.x);
m_fRightBoundary = -rect.origin.x ;
m_fTopBoundary = -rect.origin.y;
m_fBottomBoundary = -((rect.origin.y+rect.size.height) - m_obFullScreenSize.y);
if(m_fRightBoundary < m_fLeftBoundary)
{
// screen width is larger than world's boundary width
//set both in the middle of the world
m_fRightBoundary = m_fLeftBoundary = (m_fLeftBoundary + m_fRightBoundary) / 2;
}
if(m_fTopBoundary < m_fBottomBoundary)
{
// screen width is larger than world's boundary width
//set both in the middle of the world
m_fTopBoundary = m_fBottomBoundary = (m_fTopBoundary + m_fBottomBoundary) / 2;
}
if( (m_fTopBoundary == m_fBottomBoundary) && (m_fLeftBoundary == m_fRightBoundary) )
{
m_bBoundaryFullyCovered = true;
}
}
return true;
}
void FollowWithOffset::step(float dt)
{
CC_UNUSED_PARAM(dt);
if(m_bBoundarySet){
// whole map fits inside a single screen, no need to modify the position - unless map boundaries are increased
if(m_bBoundaryFullyCovered)
return;
CCPoint tempPos = ccpSub( m_obHalfScreenSize, m_pobFollowedNode->getPosition());
m_pTarget->setPosition(ccp(clampf(tempPos.x, m_fLeftBoundary, m_fRightBoundary),
clampf(tempPos.y, m_fBottomBoundary, m_fTopBoundary)));
}
else{
//custom written code to add in support for a smooth ccfollow action
CCPoint tempPos = ccpSub( m_obHalfScreenSize, m_pobFollowedNode->getPosition());
CCPoint moveVect = ccpMult(ccpSub(tempPos,m_pTarget->getPosition()),0.25); //0.25 is the smooth constant.
CCPoint newPos = ccpAdd(m_pTarget->getPosition(), moveVect);
m_pTarget->setPosition(newPos);
}
}

Related

Collision detection between layers

I have a few layers, which holds CCSprites .
I have a main CCScene that is adding these layers at the start.
I have to check collision between sprites from different layers .
I know how to check a simple collision with CGRectContains , my problem is , that i need some kind of a class that holds all the sprites positions from all layers ,and to check each tick for collision.
Question: what is the right way to create such a class,that save/check sprites from all layers on the scene ?
THanks .
If you don't care about empty space, then you can just set self.contentSize for every layer. You can do it like this:
CGRect rect = CGRectNull;
for (CCNode *node in self.children) {
rect = CGRectUnion(rect, node.boundingBox);
}
self.contentSize = rect.size;
But if you care about empty space, then you need to use something like this:
// put it somewhere outside class implementation
static inline BOOL CCLayerIntersectsCCLayer(CCLayer l1, CCLayer l2) {
for (CCNode *n1 in l1.children) {
for (CCNode *n2 in l2.children) {
CGRect r1 = n1.boundingBox;
CGRect r2 = n2.boundingBox;
r1.origin = [l1.parent convertToNodeSpace:r1.origin];
r2.origin = [l2.parent convertToNodeSpace:r2.origin];
if (CGRectIntersectsCGRect(r1, r2)) {
return YES;
}
}
}
return NO;
}
But you must understand, that all sprites (even circles) are actually rectangles. So if you need very precise collision detection, then you should use box2d in your project. If you are interesting in this, here are some links to read:
Box2d
Intro to Box2D with Cocos2D 2.X Tutorial: Bouncing Balls
Box2D Tutorial for iOS: How To Use Box2D For Just Collision Detection with Cocos2D iPhone

CCSprite anchorPoint not working as expected?

Let me start by explaining what I am trying to do. I have a full screen animation with about 73 frames and with these images being so large I am not able to use sprite sheets so I am just adding them to an animation as seperate spriteframes. My goal is to have the animation play through and then have it disappear from left to right. The way that I am achieving this look is by updating the width of the (textureRect) of the frames and eventually making the textureRect width 0. So I have set up the CCSprite that I am going to run the animation on.
-(id) init {
if( (self=[super init])) {
self.transSprite = [CCSprite spriteWithFile:#"transition0.pvr.ccz"];
if (CC_CONTENT_SCALE_FACTOR() == 1) {
//iPhone 3Gs
self.transSprite.scaleX = .563;
self.transSprite.scaleY = .665;
self.transSprite.position = ccp(self.transSprite.contentSize.height - 3, -1);
self.transSprite.anchorPoint = ccp(1, 0);
[self addChild:self.transSprite z:5];
}
else if (kiPhone5) {
//iPhone 5
self.transSprite.scale = self.transSprite.scale * 1.335f;
self.transSprite.position = ccp(569, -1);
self.transSprite.anchorPoint = ccp(1, 0);
[self addChild:self.transSprite z:5];
}
else {
//iPhone 4
self.transSprite.scaleX = 1.126;
self.transSprite.scaleY = 1.33;
self.transSprite.position = ccp(481, -1);
self.transSprite.anchorPoint = ccp(1, 0);
[self addChild:self.transSprite z:5];
}
}
return self;
}
So the reason for checking what device is running, is because I did not make 2 versions of the animation frames HD and SD. Instead I just made them a good in between size and then I check to see what device is running and scale the sprite accordingly. So here is my problem I am setting the Sprites anchorPoint to the bottom right hand corner of the screen so that when I change the width of the textureRect it will decrease in size from left to right. Everything seems to be working great with this idea except for when I change the width of the textureRect of each sprite frame.
- (void) rectUpdate3Gs {
for (CCAnimationFrame *frame in transition.frames) {
frame.spriteFrame.rect = CGRectMake(0, 0, 856, 484);
}
}
- (void) retinaRectUpdate {
for (CCAnimationFrame *frame in transition.frames) {
frame.spriteFrame. rect = CGRectMake(0, 0, 428, 242);
}
}
So when I run the animation and start to change the width of the textureRect it decreases from both the left and the right side, it is like the anchorPoint is being ignored. Do the spriteFrames have there own anchorPoint or what is happening.
Here is an illustration of what is happening.This is the CCSprite set up for the full texture and with the anchorPoint in the bottom right corner.
self.transSprite.scaleX = .563;
self.transSprite.scaleY = .665;
self.transSprite.position = ccp(self.transSprite.contentSize.height - 3, -1);
[self addChild:self.transSprite z:5];
[self.transSprite setTextureRect:CGRectMake(0, 0, 856, 484)];
self.transSprite.anchorPoint = ccp(1, 0);
Now this is setting the textureRect of the CCSprite a little smaller.
This is the way that I want it to work, is to subtract the width of the texture from the left to right. But when I change the width of the animation frames to match this subtracted width this is what I get.
I am at a loss as to why this is happening. Do the animation frames not respect the anchor point of the CCSprite they are running on?
Scaling issue aside, there's 2 things you can do. First, and probably easiest, is to simply add a CCLayerColor on top of the animation and scale that down accordingly. This way you don't need to modify every animation frame's rect.
Second option would be to use a clipping node, found here:
Here

Box2D & Cocos2d level reset issue

I have a very simple level that has a single static body (and associated sprite) in it. When the user touches the body, I remove the sprite, destroy the body and replace it with a dynamic body with a new sprite, and then I transition to a new scene. My code works perfectly the first time it is executed, but then when this scene is reloaded it crashes when the user touches the body.
I store the filename of the .png file that I use for my sprite in the sprite's userData field. Then when the user presses the button (touches the body) it is a simple matter to retrieve it. The problem is occurring on subsequent reloads of the scene when I try to access the sprite's userData field. Instead of holding the filename it is empty (null). This in turn crashes the program when I try to use the filename.
I cannot understand why it works the first time and not the second. I set a breakpoint and watch the filename getting assigned to the sprite's userData field, but it is only retrievable the first time I create the scene. Here is the ccTouchesBegan method. The crash occurs where I try to assign newSpriteFileName because on the second run through oldSpriteFileName is empty.
I have just about gone crazy with this one, but I am sure it is something obvious. As always thanks for the help!!
(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for( UITouch *touch in touches )
{
CGPoint touchLocation = [touch locationInView: [touch view]];
touchLocation = [[CCDirector sharedDirector] convertToGL: touchLocation];
touchLocation = [self convertToNodeSpace:touchLocation];
b2Vec2 locationWorld = b2Vec2(touchLocation.x/PTM_RATIO, touchLocation.y/PTM_RATIO);
NSLog(#"Location x = %f, y = %f", touchLocation.x, touchLocation.y);
b2AABB aabb;
b2Vec2 delta = b2Vec2(1.0/PTM_RATIO, 1.0/PTM_RATIO);
aabb.lowerBound = locationWorld - delta;
aabb.upperBound = locationWorld +delta;
SimpleQueryCallback callback(locationWorld);
m_world->QueryAABB(&callback, aabb);
//If they have not pressed a button yet, and this touch was actually inside
//one of the buttons then we will destroy the static button (and sprite)
//that they touched and replace it with a dynamic button
//(and new darker sprite) that falls, spinning off of the screen.
if(!replaceButtonPushedYet && callback.fixtureFound)
{
//Get a reference to the static body of the button they touched and wake it up
b2Body *body = callback.fixtureFound->GetBody();
body->SetAwake(true);
//Get the position of the body (button) they touched and use it to
//position the new button. This value is in Box2D coordinates,
//so when I position the new button I won't divide by PTM_RATIO.
b2Vec2 touchedButtonPosition = body->GetPosition();
//Get the sprite from the button the user pressed
CCSprite *bodySprite = (CCSprite*)body->GetUserData();
//Then extract the file name of the sprite. I assigned this to
//sprite's userData when I loaded the sprite in the method //"placeTheIndividualButton". Now I am going to extract it and
//then replace "(up)" with "(down)" in that string
//and that becomes the name of the sprite for the new (pressed)
//button I am getting ready to create. It is all about the file
//naming conventions!
NSString *oldSpriteFileName = (NSString*)bodySprite.userData;
NSString *newSpriteFileName = [oldSpriteFileName stringByReplacingOccurrencesOfString:#"up" withString:#"down"];
//First remove the sprite tied to the button the user pressed,
//then destroy the body of the button.
[self removeChild:bodySprite cleanup:YES];
body->GetWorld()->DestroyBody(body);
//Set the bool to true to keep this code from executing again.
//This ensures that once they press a button they can't
//press another one.
replaceButtonPushedYet = true;
//Build the new dynamic button that will fall and spin off the screen
b2BodyDef buttonBodyDef2;
b2PolygonShape buttonShape2;
b2FixtureDef buttonShapeDef2;
b2Vec2 vertices2[4];
//load the sprite for the second button (pressed down) and add it to the layer
level_down = [CCSprite spriteWithFile:newSpriteFileName];
level_down.userData=newSpriteFileName;
[self addChild:level_down];
//Define the polygon that forms the second button (pressed down)
buttonBodyDef2.type = b2_dynamicBody;
//Not dividing touchedButtonPosition.x or .y by PTM_RATIO because
//they are already in Box2D coordinates
buttonBodyDef2.position.Set(touchedButtonPosition.x, touchedButtonPosition.y);
buttonBodyDef2.angularVelocity = 1.5;
buttonBodyDef2.userData = level_down;
buttonBody2 = m_world->CreateBody(&buttonBodyDef2);
//Define the vertices for the replacement button
vertices2[0].Set(-94/PTM_RATIO, -32/PTM_RATIO);
vertices2[1].Set(94/PTM_RATIO, -32/PTM_RATIO);
vertices2[2].Set(94/PTM_RATIO, 32/PTM_RATIO);
vertices2[3].Set(-94/PTM_RATIO, 32/PTM_RATIO);
buttonShape2.Set(vertices2, 4);
//Define the shape for the replacement button
buttonShapeDef2.shape = &buttonShape2;
buttonShapeDef2.density = 50.01f;
buttonShapeDef2.friction = 0.75f;
buttonShapeDef2.restitution = 0.1f;
//The static buttons and the dynamic buttons are both in this groupIndex.
//Since it is a negative number they will never collide. If it was
//positive they would always collide.
buttonShapeDef2.filter.groupIndex = -1;
//Put the second button (pressed down) into the world.
buttonBody2->CreateFixture(&buttonShapeDef2);
//This starts a timer that fires every second to call the makeTransition
//method. The code inside that method will check to see if the button
//has fallen off the screen yet. If it has then it will transition to
//the new selected level.
//The colon after makeTransition sends a ccTime (dt). I don't need it,
//but may in the future so I left it in there.
buttonFalling = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(makeTransition:) userInfo:nil repeats:YES];
}
}
}
Chances are that the NSString is being released when you remove the sprite. Why not just storing the texture name in an instance variable?

Can I make CCFollow follow more naturally?

I want to build a platform game with cocos2d/Box2D. I use CCFollow to follow the player sprite but CCFollow constantly puts it in the center of the screen. I want CCFollow to follow more naturally, like a human turning a camcorder with an acceptable lag, a small overshoot ...etc.
Here is a method of mine that didn't work: I attached (via a distance joint) a small physics body to the player that doesn't collide with anything, represented by a transparent sprite. I CCFollow'ed the transparent sprite. I was hoping this ghost body would act like a balloon attached to the player, hence a smooth shift in view. The problem is distance joint breaks with too heavy - too light objects. The balloon moves around randomly, and of course, it pulls the player back a little no matter how light it is.
What is a better way of following a moving sprite smoothly?
Try add this to CCActions in cocos2d libs.
-(void) step:(ccTime) dt
{
#define CLAMP(x,y,z) MIN(MAX(x,y),z)
CGPoint pos;
if(boundarySet)
{
// whole map fits inside a single screen, no need to modify the position - unless map boundaries are increased
if(boundaryFullyCovered) return;
CGPoint tempPos = ccpSub(halfScreenSize, followedNode_.position);
pos = ccp(CLAMP(tempPos.x,leftBoundary,rightBoundary), CLAMP(tempPos.y,bottomBoundary,topBoundary));
}
else {
// pos = ccpSub( halfScreenSize, followedNode_.position );
CCNode *n = (CCNode*)target_;
float s = n.scale;
pos = ccpSub( halfScreenSize, followedNode_.position );
pos.x *= s;
pos.y *= s;
}
CGPoint moveVect;
CGPoint oldPos = [target_ position];
double dist = ccpDistance(pos, oldPos);
if (dist > 1){
moveVect = ccpMult(ccpSub(pos,oldPos),0.05); //0.05 is the smooth constant.
oldPos = ccpAdd(oldPos, moveVect);
[target_ setPosition:oldPos];
}
#undef CLAMP
}
i get this from cocos2d forums.
Perhaps this http://www.cocos2d-iphone.org/wiki/doku.php/prog_guide:actions_ease can help you get an "acceleration" effect with CCFollow.

Moving Box2d Bodies Like CCSprite Objects

In cocos2d, you can ease in CCSprites and move them around in all kinds of ways. Most importantly - they can have easing in/out. For most games this is desirable for smooth movement etc.
id action = [CCMoveTo actionWithDuration:dur position:pos];
move = [CCEaseInOut actionWithAction:action rate:2];
[self runAction: move];
When moving a box2d body, the sprite attached to it is updated after the box2d step(). Moving the sprite and then updating the body is not an option here, as it entirely defeats the purpose of the physics framework.
So the other option, which I have successfully implemented, is to calculate the displacement, velocity and acceleration of a sprite by treating it as a mechanics entity in its own right. Each time I call my update() on the sprite so the character can decide where to move etc, my superclass also stores the previous position and velocity. These are stored as box2d compliant values by dividing by the PTM_RATIO.
In the subclass of CCSprite, called FMSprite:
-(CGPoint) displacement {
return ccpSub(self.position, lastPos);
}
-(b2Vec2) getSpriteVelocity:(ccTime)dt {
return b2Vec2(self.displacement.x / dt / PTM_RATIO,
self.displacement.y / dt / PTM_RATIO);
}
-(b2Vec2) getSpriteAccel:(ccTime)dt {
b2Vec2 currVel = [self getSpriteVelocity:dt];
if (dt == 0) {
return b2Vec2(0,0);
} else {
float accelX = (currVel.x - lastVel.x)/dt;
float accelY = (currVel.y - lastVel.y)/dt;
return b2Vec2(accelX, accelY);
}
}
// This is called each update()
-(void) updateLast:(ccTime)dt {
// MUST store lastVel before lastPos is updated since it uses displacement
lastVel = [self getSpriteVelocity:dt];
lastPos = ccp(self.X, self.Y);
}
// Leave this method untouched in subclasses
-(void) update:(ccTime)dt {
[self updateObject:dt];
// Store previous update values
[self updateLast:dt];
}
// Override this method in subclasses for custom functionality
-(void) updateObject:(ccTime)dt {
}
I have then subclassed "FMSprite" into "FMObject", which stores a b2Body etc.
In order to move the body, I must first move a sprite and track its acceleration, through which I can find the required force (using the mass) needed to follow the sprite's motion. Since I can't move the object's sprite (which is synchronized to the body), I make another sprite called a "beacon", add it as a child to the object, and move it around. All we need to do then is to have a function to synchronize the position of the box2d body with this beacon sprite using the forces I mentioned before.
-(void) followBeaconWithDelta:(ccTime)dt {
float forceX = [beacon getSpriteAccel:dt].x * self.mass;
float forceY = [beacon getSpriteAccel:dt].y * self.mass;
[self addForce:b2Vec2(forceX, forceY)];
}
The result is brilliant, a smooth easing motion of the b2body moving where ever you want it to, without playing around with any of its own forces, but rather copying that of a CCSprite and replicating its motion. Since it's all forces, it won't cause jittering and distortions when colliding with other b2Body objects. If anyone has any other methods to do this, please post an answer. Thanks!
What I do is different from yours, but can also Moving Box2d Bodies Like CCSprite Objects and even use the CCAction.
The most important thing is to create an object that contain ccSprite and b2body.
#interface RigidBody : CCNode {
b2Body *m_Body;
CCSprite *m_Sprite;
}
And then, rewrite the setPosition method.
-(void)setPosition:(CGPoint)position
{
CGPoint currentPosition = position_;
b2Transform transform = self.body->GetTransform();
b2Vec2 p = transform.p;
float32 angle = self.body->GetAngle();
p += [CCMethod toMeter:ccpSub(position, currentPosition)];
self.body->SetTransform(p, angle);
position_ = position;
}
The setPosition method calculate how much the position change,and set it to the b2body.
I hope I have understanding your question and the answer is helpful for you...