I am a new programmer in cocos2D and I tried to do the tutorial https://www.makegameswith.us/tutorials/getting-started-with-spritebuilder/creating-two-levels/
but my Object just fly down on the ground and not right... I don't know what wrong is.
here is my Code:
- (void)launchFire {
// loads the Penguin.ccb we have set up in Spritebuilder
CCNode* fireBall = [CCBReader load:#"Fireball"];
// position the penguin at the bowl of the catapult
fireBall.position = ccpAdd(_block.position, ccp(18, 50));
CCLOG(#"Block Position: \nX:%.2f\nY:%.2f",_block.position.x,_block.position.y);
CCLOG(#"Fireball Position: \nX:%.2f\nY:%.2f",fireBall.position.x,fireBall.position.y);
// add the penguin to the physicsNode of this scene (because it has physics enabled)
[_physicsNode addChild:fireBall];
// manually create & apply a force to launch the penguin
CGPoint launchDirection = ccp(1, 0);
CGPoint force = ccpMult(launchDirection, 8000);
[fireBall.physicsBody applyForce:force];
}
I used the Variable Fireball and not Pinguin...
If you can help me, many many Thanks.
Related
I just started using SpriteBuilder with cocos2d - v3.
It looks promising and a good tool to have.
But my issue is that I want to create a man like character with all body parts as different sprites and put them into a Physics body using joints. I am struggling to find any example of it using spritebuilder or chipmunk.
Will greatly appreciate any help here. Thx in advance.
EDIT : Here's code for body. I used peevedpenquin example from link
Here is my code for character body. I just added a block as body part to penguin's tail.
#import "PenguinWithBlock.h"
#implementation PenguinWithBlock{
CCNode *_pBlock;
CCNode *_penguinB;
CCPhysicsJoint *_penguinJoint;
CCPhysicsNode *_penguinNode;
}
- (void)didLoadFromCCB {
_penguinNode.debugDraw = TRUE;
[_pBlock.physicsBody setCollisionGroup:_penguinB];
[_penguinB.physicsBody setCollisionGroup:_penguinB];
// create a joint to connect the catapult arm with the catapult
_penguinJoint = [CCPhysicsJoint connectedPivotJointWithBodyA:_penguinB.physicsBody bodyB:_pBlock.physicsBody anchorA:_penguinB.anchorPointInPoints];
}
This is Gameplay.m file
#implementation Gameplay {
CCPhysicsNode *_physicsNode;
CCNode *_catapultArm;
CCNode *_catapult;
CCNode *_levelNode;
CCNode *_contentNode;
CCPhysicsJoint *_catapultJoint;
CCNode *_pullbackNode;
CCPhysicsJoint *_pullbackJoint;
CCNode *_mouseJointNode;
CCPhysicsJoint *_mouseJoint;
PenguinWithBlock *_currentPenguin;
CCPhysicsJoint *_penguinCatapultJoint;
CCAction *_followPenguin;
}
// is called when CCB file has completed loading
- (void)didLoadFromCCB {
// tell this scene to accept touches
self.userInteractionEnabled = TRUE;
_physicsNode.collisionDelegate = self;
CCScene *level = [CCBReader loadAsScene:#"Levels/Level1"];
[_levelNode addChild:level];
// visualize physics bodies & joints
//_physicsNode.debugDraw = TRUE;
// catapultArm and catapult shall not collide
[_catapultArm.physicsBody setCollisionGroup:_catapult];
[_catapult.physicsBody setCollisionGroup:_catapult];
// create a joint to connect the catapult arm with the catapult
_catapultJoint = [CCPhysicsJoint connectedPivotJointWithBodyA:_catapultArm.physicsBody bodyB:_catapult.physicsBody anchorA:_catapultArm.anchorPointInPoints];
// nothing shall collide with our invisible nodes
_pullbackNode.physicsBody.collisionMask = #[];
// create a spring joint for bringing arm in upright position and snapping back when player shoots
_pullbackJoint = [CCPhysicsJoint connectedSpringJointWithBodyA:_pullbackNode.physicsBody bodyB:_catapultArm.physicsBody anchorA:ccp(0, 0) anchorB:ccp(34, 138) restLength:60.f stiffness:500.f damping:40.f];
_mouseJointNode.physicsBody.collisionMask = #[];
}
- (void)update:(CCTime)delta
{
if (_currentPenguin.launched) {
// if speed is below minimum speed, assume this attempt is over
if (ccpLength(_currentPenguin.physicsBody.velocity) < MIN_SPEED){
[self nextAttempt];
return;
}
int xMin = _currentPenguin.boundingBox.origin.x;
if (xMin < self.boundingBox.origin.x) {
[self nextAttempt];
return;
}
int xMax = xMin + _currentPenguin.boundingBox.size.width;
if (xMax > (self.boundingBox.origin.x + self.boundingBox.size.width)) {
[self nextAttempt];
return;
}
}
}
- (void)nextAttempt {
_currentPenguin = nil;
[_contentNode stopAction:_followPenguin];
CCActionMoveTo *actionMoveTo = [CCActionMoveTo actionWithDuration:1.f position:ccp(0, 0)];
[_contentNode runAction:actionMoveTo];
}
// called on every touch in this scene
-(void) touchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchLocation = [touch locationInNode:_contentNode];
// start catapult dragging when a touch inside of the catapult arm occurs
if (CGRectContainsPoint([_catapultArm boundingBox], touchLocation))
{
// move the mouseJointNode to the touch position
_mouseJointNode.position = touchLocation;
// setup a spring joint between the mouseJointNode and the catapultArm
_mouseJoint = [CCPhysicsJoint connectedSpringJointWithBodyA:_mouseJointNode.physicsBody bodyB:_catapultArm.physicsBody anchorA:ccp(0, 0) anchorB:ccp(34, 138) restLength:0.f stiffness:3000.f damping:150.f];
// create a penguin from the ccb-file
_currentPenguin = (PenguinWithBlock*)[CCBReader load:#"PenguinWithBlock"];
// initially position it on the scoop. 34,138 is the position in the node space of the _catapultArm
CGPoint penguinPosition = [_catapultArm convertToWorldSpace:ccp(34, 138)];
// transform the world position to the node space to which the penguin will be added (_physicsNode)
_currentPenguin.position = [_physicsNode convertToNodeSpace:penguinPosition];
// add it to the physics world
[_physicsNode addChild:_currentPenguin];
// we don't want the penguin to rotate in the scoop
_currentPenguin.physicsBody.allowsRotation = FALSE;
// create a joint to keep the penguin fixed to the scoop until the catapult is released
_penguinCatapultJoint = [CCPhysicsJoint connectedPivotJointWithBodyA:_currentPenguin.physicsBody bodyB:_catapultArm.physicsBody anchorA:_currentPenguin.anchorPointInPoints];
}
}
- (void)touchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
// whenever touches move, update the position of the mouseJointNode to the touch position
CGPoint touchLocation = [touch locationInNode:_contentNode];
_mouseJointNode.position = touchLocation;
}
- (void)launchPenguin {
// loads the Penguin.ccb we have set up in Spritebuilder
CCNode* penguin = [CCBReader load:#"PenguinWithBlock"];
// position the penguin at the bowl of the catapult
penguin.position = ccpAdd(_catapultArm.position, ccp(16, 50));
// add the penguin to the physicsNode of this scene (because it has physics enabled)
[_physicsNode addChild:penguin];
// manually create & apply a force to launch the penguin
CGPoint launchDirection = ccp(1, 0);
CGPoint force = ccpMult(launchDirection, 8000);
[penguin.physicsBody applyForce:force];
// ensure followed object is in visible are when starting
self.position = ccp(0, 0);
CCActionFollow *follow = [CCActionFollow actionWithTarget:penguin worldBoundary:self.boundingBox];
[_contentNode runAction:follow];
}
-(void) touchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
// when touches end, meaning the user releases their finger, release the catapult
[self releaseCatapult];
}
-(void) touchCancelled:(UITouch *)touch withEvent:(UIEvent *)event
{
// when touches are cancelled, meaning the user drags their finger off the screen or onto something else, release the catapult
[self releaseCatapult];
}
- (void)releaseCatapult {
if (_mouseJoint != nil)
{
// releases the joint and lets the catapult snap back
[_mouseJoint invalidate];
_mouseJoint = nil;
// releases the joint and lets the penguin fly
[_penguinCatapultJoint invalidate];
_penguinCatapultJoint = nil;
// after snapping rotation is fine
_currentPenguin.physicsBody.allowsRotation = TRUE;
_currentPenguin.launched = TRUE;
// follow the flying penguin
_followPenguin = [CCActionFollow actionWithTarget:_currentPenguin worldBoundary:self.boundingBox];
[_contentNode runAction:_followPenguin];
}
}
-(void)ccPhysicsCollisionPostSolve:(CCPhysicsCollisionPair *)pair seal:(CCNode *)nodeA wildcard:(CCNode *)nodeB
{
float energy = [pair totalKineticEnergy];
// if energy is large enough, remove the seal
if (energy > 5000.f)
{
[self sealRemoved:nodeA];
}
}
- (void)sealRemoved:(CCNode *)seal {
// load particle effect
CCParticleSystem *explosion = (CCParticleSystem *)[CCBReader load:#"SealExplosion"];
// make the particle effect clean itself up, once it is completed
explosion.autoRemoveOnFinish = TRUE;
// place the particle effect on the seals position
explosion.position = seal.position;
// add the particle effect to the same node the seal is on
[seal.parent addChild:explosion];
// finally, remove the destroyed seal
[seal removeFromParent];
}
- (void)retry {
// reload this level
[[CCDirector sharedDirector] replaceScene: [CCBReader loadAsScene:#"Gameplay"]];
}
PenguinWithBlock settings in SpriteBuilder:
You can use a "blank" CCNode with all others parts of the body being children of it, maybe even have children of another body parts. The tree of nodes will heavily depend on the body design.
Give a look at this tutorial, as it teaches the basics to do so. Also, SpriteBuilder still doesn't have the feature to create the joints inside it, but you can easily do this programmatically as the tutorial does.
EDIT: This can help you
I just started using the SpriteBuilder with Cocos2d-v3
For the exercise of the SpriteBuilder, I practice in the tutorial of the following WebSite
for the time being.
https://www.makegameswith.us/tutorials/getting-started-with-spritebuilder/collision-detection/
As a flow of the games of the tutorials,at first the catapult and the catapultArm are located on stage,the object(Penguin) which was attached to the arm flies forward when I pull the arm to the rear with a finger and separate a finger from a screen.
When the object(Penguin) hits other objects(is called Seal),output log,"Something collided with a seal!"
In a "Implementing a delegate method" of tutorial, I was going to try to display a letter,"Something collided with a seal!" in Consoll using CCLOG method.
In the Gameplay.m, I implemented ccPhysicsCollisionPostSolve:seal:wildcard method that is called to dispaly a letter "Something collide with a seal!",but this method isn't called when Penguin object hits other objects(seal).
I certainly made Penguin.ccbi and Seal.ccbi in SpriteBuilder,and thier ccbi file is Physics body.
Why ccPhysicsCollisionPostSolve:seal:wildcard method isn't called ?
It's my code that I really implemented as follows
This is Gameplay.m file
#import "Gameplay.h"
#implementation Gameplay{
CCPhysicsNode *_physicsNode;
//To joint betweent catapult and catapultArm
CCNode *_catapultArm;
CCNode *_catapult;
CCPhysicsJoint *_catapultJoint;
//Invisible Physics force
CCNode *_pullbackNode;
CCPhysicsJoint *_pullbackJoint;
//to move catapultArm
CCNode *_mouseJointNode;
CCPhysicsJoint *_mouseJoint;
//to fly penguin
CCNode *_currentPenguin;
CCPhysicsJoint *_penguinCatapultJoint;
//Object
CCNode *_levelNode;
//To Prevent a 'retry' button from moving with a fly penguin
CCNode *_contentNode;
}
//is called when CCB file has completed loading
-(void)didLoadFromCCB{
_physicsNode.collisionDelegate = self;
//tell this scene to accept touches
self.userInteractionEnabled = TRUE;
//loads the Levels/Leve1.ccb we have set up in SpriteBuilder
CCScene *level = [CCBReader loadAsScene:#"Levels/Level1"];
[_levelNode addChild:level];
//visualize physics bodies & joints
_physicsNode.debugDraw = TRUE;
//catapultArm and catapult shall not collide
[_catapultArm.physicsBody setCollisionGroup:_catapult];
[_catapult.physicsBody setCollisionGroup:_catapult];
//create a joint to connect the catapult arm with the catapult
_catapultJoint = [CCPhysicsJoint connectedPivoJointWithBodyA:_catapultArm.physicsBody
bodyB:_catapult.physicsBody
anchorA:_catapultArm.anchorPointInPoints];
//nothing shall collide with our invisible nodes
_pullbackNode.physicsBody.collisionMask = #[];
//nothing shall collide with our invisible nodes
_mouseJointNode.physicsBody.collisionMask = #[];
_pullbackJoint = [CCPhysicsJointconnectedSpringJointWithBodyA:_pullbackNode.physicsBody
bodyB:_catapultArm.physicsBody
anchorA:ccp(0,0)
anchorB:ccp(34,138)
restLength:60.f
stiffness:500.f
damping:40.f
];
}
//called on every touch in this scene (called every touch)
-(void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event{
CCLOG(#"touch Began");
CGPoint touchLcation = [touch locationInNode:_contentNode];
//start catapult dragging when a touch inside of the catapult arm occurs
if(CGRectContainsPoint([_catapultArm boundingBox], touchLcation))
{
//move the mouseJointNode to the touch position
_mouseJointNode.position = touchLcation;
_mouseJoint = [CCPhysicsJoint connectedSpringJointWithBodyA:_mouseJointNode.physicsBody
bodyB:_catapultArm.physicsBody
anchorA:ccp(0,0)
anchorB:ccp(34,138)
restLength:0.f
stiffness:3000.f
damping:150.f
];
//create a penguin from the ccbFile
_currentPenguin = [CCBReader load:#"Penguin"];
CGPoint penguinPosition = [_catapultArm convertToWorldSpace:ccp(34, 138)];
_currentPenguin.position = [_physicsNode convertToNodeSpace:penguinPosition];
//add it to the physics world
[_physicsNode addChild:_currentPenguin];
//we don't want the penguin to rotate in the scoop
_currentPenguin.physicsBody.allowsRotation = FALSE;
//create a joint to keep the penguin fixed to the scoop until the catapult is released
_penguinCatapultJoint =
[CCPhysicsJoint connectedPivoJointWithBodyA:_currentPenguin.physicsBody
bodyB:_catapultArm.physicsBody
anchorA:_currentPenguin.anchorPointInPoints
];
}
}
-(void)touchMoved:(UITouch *)touch withEvent:(UIEvent *)event{
CCLOG(#"MOVING!!!!!!!!!!");
//whenever touches move,update the position of the mousejointNode to touch position
CGPoint touchLocation = [touch locationInNode:_contentNode];
_mouseJointNode.position = touchLocation;
}
-(void)touchEnded:(UITouch *)touch withEvent:(UIEvent *)event{
//when touches end,meaning the user releases their finger,release the catapult
[self releaseCatapult];
}
-(void)touchCancelled:(UITouch *)touch withEvent:(UIEvent *)event{
[self releaseCatapult];
}
//destroy our joint and let the catapult snap when a touch ends
-(void)releaseCatapult{
CCLOG(#"Release");
if(_mouseJoint != nil)
{
//releases the joint and lets the catapult snap back
[_mouseJoint invalidate];
_mouseJoint = nil;
//release the joint and lets the penguin fly
[_penguinCatapultJoint invalidate];
_penguinCatapultJoint = nil;
//after snapping rotation is fine
_currentPenguin.physicsBody.allowsRotation = TRUE;
//follow the flying penguin
CCActionFollow *follow =
}
}
-(void)retry
{
[[CCDirector sharedDirector]replaceScene:[CCBReader loadAsScene:#"Gameplay"]];
}
-(void)ccPhysicsCollisionPostSolve:(CCPhysicsCollisionPair *)pair seal:(CCNode *)nodeA
wildcard:(CCNode *)nodeB
{
CCLOG(#"Something collided with a seal!");
}
#end
Most likely you did not set a collision type for the Sealclass.
Double check that you have:
Set the custom class for the Seal in SpriteBuilder to Seal
Have set the collisionType within the didLoadFromCCB method inside the Seal class to "seal"
That should fix you issue.
Also, in Spritebuilder, make sure the seals already loaded in the level (Level1.ccb) have Collision type set to seal.
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?
I think I'm just understanding scaling/positioning nodes/layers incorrectly. I'm setting up my node like this (node class is derived from CCNode):
-(id) init
{
if ((self = [super init]))
{
// Create parallax background node.
background = [BackgroundNode node];
[self addChild:background z:0];
// Create foreground node.
foreground = [ForegroundLayer node];
[self addChild:foreground z:0];
self.position.y = 500.0f;
self.scaleX = 1.5f;
self.scaleY = 1.5f;
}
return self;
}
It doesn't seem to matter what I set the self.position.y to - the scaled node is always displayed as though it was positioned in the bottom-left of the screen. I've tried playing around with anchorPoint as well, but it doesn't really seem to change anything.
The reason I'm asking is because I'd like to be able to pan vertically when I'm zoomed in - this doesn't seem to really be the right way to accomplish it, though. Any ideas?
self.position.y = 500.0f;
doesn't work. It should be
self.position = ccp(self.position.x, 500.0f);
Please refer to "Cocoa Objective-c Property C structure assign fails".
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);
}
}