CCFollow – With Smooth Moving [Solution]

Forums Programming cocos2d support (graphics engine) CCFollow – With Smooth Moving [Solution]

This topic contains 33 replies, has 16 voices, and was last updated by  cocojoe 4 months, 4 weeks ago.

Viewing 25 posts - 1 through 25 (of 34 total)
Author Posts
Author Posts
May 3, 2011 at 12:18 pm #230976

i9mobile
@i9mobile

Hi Guys,

We’ve worked on a better solution for the CCFollow “-(void) step:(ccTime) dt” method.

We are trying to get a smooth moving and target re-center after changing the layer position. You can try our solution by replacing the code for the step method at line 328 on CCAction.m file:

-(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 );
}

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
}

Feel free to add your comment. The next step is to recalculate the WorldRect based on the Layer Scale, so we can use CCFollow with Zoom in or out.

Thanks Cocos2D!

Luiz Menezes (@i9mobile) & Paulo Candolo (@paulonogueira)

Reach us @IpanemaGames

May 3, 2011 at 1:10 pm #327940

n0va010
@n0va010

Nice, gonna try this later for sure! How fast does the recenter happen? Can you control the speed? I don’t have time to look through the code right now, will do later though, thanks anyways, nice contribution!

May 3, 2011 at 1:27 pm #327941

i9mobile
@i9mobile

You can control the speed by changing the smooth constant.

0.05 worked for me on my WorldRect size.

A higher number means that the movement is faster.

May 3, 2011 at 1:46 pm #327942

n0va010
@n0va010

Alright, thank you very much! I tried it now, it works like awesome :P

May 3, 2011 at 7:09 pm #327943

i9mobile
@i9mobile

Thanks, if we have any update I’ll let you know!

May 5, 2011 at 8:28 pm #327944

alexasha
Participant
@alexasha

Wow! Thanks a lot!!!

Works super awesome!!!

Can you please explain what each line of code does? I would like to change the movement a bit.

You should pm riq about your CCCamera update. He should definitely use your code in 1.0 final.

I will add you to the special thanks in my game.

June 8, 2011 at 3:09 pm #327945

conor
Participant
@conor

@i9mobile were you able to get his working with zoom?

June 20, 2011 at 1:38 pm #327946

i9mobile
@i9mobile

It doesn’t work with zoom because we aren’t updating the boundaries size. But we expect to work more on this class on our next game.

June 20, 2011 at 1:43 pm #327947

i9mobile
@i9mobile

By the way, the game using the CCFollow class was released: http://www.cocos2d-iphone.org/forum/topic/17823

July 12, 2011 at 5:25 pm #327948

cocos2dbeginner
Participant
@cocos2dbeginner

@i9mobile could you give us some tips or even the code to support scaled layers/zooming?

By the way Smelly Cat is great :).

July 12, 2011 at 5:37 pm #327949

cocos2dbeginner
Participant
@cocos2dbeginner

Solution (with smooth..)

-(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 {
CCNode *n = (CCNode*)target_;
float s = n.scale;
pos = ccpSub( halfScreenSize, followedNode_.position );
pos.x *= s;
pos.y *= s;

//pos = ccpSub( halfScreenSize, followedNode_.position );
}

CGPoint moveVect;

CGPoint oldPos = [target_ position];
double dist = ccpDistance(pos, oldPos);
if (dist > 1){
moveVect = ccpMult(ccpSub(pos,oldPos),0.2); //0.05 is the smooth constant.
oldPos = ccpAdd(oldPos, moveVect);
[target_ setPosition:oldPos];
}

#undef CLAMP
}

July 12, 2011 at 5:40 pm #327950

Stepan Generalov
Moderator
@ipsi

Probably this should be merged in cocos2d-iPhone repo. Is it possible?

July 12, 2011 at 6:17 pm #327951

i9mobile
@i9mobile

cocos2dbeginner We have changed the initWithTarget: method to receive the smooth constant, also if the horizontal or vertical following is allowed.

Hi Stepan, how we can sugest this mod to cocos2d-iPhone main repo?

July 12, 2011 at 9:29 pm #327952

Stepan Generalov
Moderator
@ipsi

Make a pull request. Or create an issue on http://code.google.com/p/cocos2d-iphone/issues/list

New CCFollow should be compatible with the old one and don’t have any new issues, IMHO.

But you’re better ask Riq.

July 12, 2011 at 10:07 pm #327953

MikeSz
Participant
@mikesz

Yes, please do !

I wrote CCFollow as part of a project that never went beyond an early prototype and I feel really bad that for many people it doesn’t work the way they would like it too. So anything that would fix or improve it is greatly appreciated :)

July 13, 2011 at 12:57 am #327954

i9mobile
@i9mobile

Thanks Stepan , I’ll turn it compatible with the old version, adding the new init methods then I’ll make a pull request.

July 29, 2011 at 7:19 am #327955

alexasha
Participant
@alexasha

It looks like I have found a solution for CCFollow with Smooth Moving to support scaling.

-(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

}

July 29, 2011 at 10:07 am #327956

lerkan
@lerkan

hi, thanks for the code… I get some jumpy movements though. I have a ball the bounces around, and sometimes it’s smooth, and sometimes the scroll “snaps” into place. How do you fix that?

Thanks

November 10, 2012 at 3:00 am #327957

gatorsmile
@gatorsmile

Tried using your new step method in a Kobold2d/Box2d project and got 2 compile time errors on the CGPoint oldPos = [target_ position]; line:

1. Initializing ‘CGPoint’ with an expression of incompatible type ‘AVCaptureDevicePosition’

2. Multiple methods named position found

Has anyone come across this problem?

Thanks!

November 10, 2012 at 11:39 pm #327958

KAMIKAZE
Participant
@kamikaze

Added code works OK on cocos2d v 2.0 Stable. However it works not as expected.

When I use code “[self runAction: [CCFollow actionWithTarget: ….. ” it’s following better than original, but my sprite position always on the right of the screen. Can anyone reproduce same?

November 10, 2012 at 11:44 pm #327959

KAMIKAZE
Participant
@kamikaze

Ah, ok I founded what is it. I have a box2d world and I’m do following to my car in the game, and it moves too fast than CCFollow can follow to it :) so it’s always at the right at the screen. But when car stops moving, camera moves to center. Now I need to fix this..

http://img707.imageshack.us/img707/8558/2012111114614.png

November 17, 2012 at 8:40 am #327960

Hexo
Participant
@hexo55

Hi guys! I’ve edited the code here to support scaling with boundary set.

//
// CCFollowDynamic.h
// UnknownGame
//
// Created by hexo55 on 11/17/12.
//
//

#import "cocos2d.h"

@interface CCFollowDynamic : CCAction
{
/* node to follow */
CCNode *followedNode_;

/* whether camera should be limited to certain area */
BOOL boundarySet;

/* fast access to the screen dimensions */
CGPoint halfScreenSize;
CGPoint fullScreenSize;

/* world boundaries */
float leftBoundary;
float rightBoundary;
float topBoundary;
float bottomBoundary;

/* used for smoothing follow action */
BOOL smoothingEnabled;
float smoothingFactor;
}

/** alter behavior - turn on/off boundary */
@property (nonatomic,readwrite) BOOL boundarySet;

/** creates the action with no boundary set */
+(id) actionWithTarget:(CCNode *)followedNode;

/** creates the action with no boundary and with a smoothing factor
@param factor factor used for smoothing the follow action. Recommended
values are from (0.0f, 1.0f]. A low value provides slow smoothing while a
value of 1.0f has no smoothing at all.
*/
+(id) actionWithTarget:(CCNode *)followedNode smoothingFactor:(float)factor;

/** creates the action with a set boundary */
+(id) actionWithTarget:(CCNode *)followedNode worldBoundary:(CGRect)rect;

/** creates the action with a set boundary and with a smoothing factor */
+(id) actionWithTarget:(CCNode *)followedNode worldBoundary:(CGRect)rect smoothingFactor:(float)factor;

/** initializes the action */
-(id) initWithTarget:(CCNode *)followedNode;

/** initializes the action with a set boundary */
-(id) initWithTarget:(CCNode *)followedNode worldBoundary:(CGRect)rect;

/** initializes the action with smoothing factor */
- (id) initWithTarget:(CCNode *)followedNode smoothingFactor:(float)factor;

/** initializes the action with a set boundary and with a smoothing factor */
- (id) initWithTarget:(CCNode *)followedNode worldBoundary:(CGRect)rect smoothingFactor:(float)factor;

@end

//
// CCFollowDynamic
// UnknownGame
//
// Created by hexo55 on 11/17/12.
//
//

#import "CCFollowDynamic.h"

@implementation CCFollowDynamic

@synthesize boundarySet;

+(id) actionWithTarget:(CCNode *) fNode
{
return [[[self alloc] initWithTarget:fNode] autorelease];
}

+(id) actionWithTarget:(CCNode *)fNode smoothingFactor:(float)factor
{
return [[[self alloc] initWithTarget:fNode smoothingFactor:factor] autorelease];
}

+(id) actionWithTarget:(CCNode *) fNode worldBoundary:(CGRect)rect
{
return [[[self alloc] initWithTarget:fNode worldBoundary:rect] autorelease];
}

+ (id) actionWithTarget:(CCNode *)fNode worldBoundary:(CGRect)rect smoothingFactor:(float)factor
{
return [[[self alloc] initWithTarget:fNode worldBoundary:rect smoothingFactor:factor] autorelease];
}

-(id) initWithTarget:(CCNode *)fNode
{
return [self initWithTarget:fNode smoothingFactor:0.0f];
}

- (id) initWithTarget:(CCNode *)fNode smoothingFactor:(float)factor
{
if( (self=[super init]) ) {

followedNode_ = [fNode retain];
boundarySet = FALSE;

CGSize s = [[CCDirector sharedDirector] winSize];
fullScreenSize = CGPointMake(s.width, s.height);
halfScreenSize = ccpMult(fullScreenSize, .5f);

smoothingFactor = factor;
smoothingEnabled = YES;
if ( (fabs(smoothingFactor-0.0f)<0.001) )
{
smoothingEnabled = NO;
}
}

return self;
}

-(id) initWithTarget:(CCNode *)fNode worldBoundary:(CGRect)rect
{
return [self initWithTarget:fNode worldBoundary:rect smoothingFactor:0.0f];
}

- (id) initWithTarget:(CCNode *)fNode worldBoundary:(CGRect)rect smoothingFactor:(float)factor
{
if( (self=[super init]) ) {
followedNode_ = [fNode retain];
boundarySet = TRUE;

CGSize winSize = [[CCDirector sharedDirector] winSize];
fullScreenSize = CGPointMake(winSize.width, winSize.height);
halfScreenSize = ccpMult(fullScreenSize, .5f);

leftBoundary = rect.origin.x;
rightBoundary = rect.origin.x+rect.size.width ;
topBoundary = rect.origin.y+rect.size.height;
bottomBoundary = rect.origin.y;

smoothingFactor = factor;
smoothingEnabled = YES;
if ( (fabs(smoothingFactor-0.0f)<0.001) )
{
smoothingEnabled = NO;
}
}

return self;
}

-(id) copyWithZone: (NSZone*) zone
{
CCAction *copy = [[[self class] allocWithZone: zone] init];
copy.tag = tag_;
return copy;
}

-(void) step:(ccTime) dt
{
CGPoint pos;
float s = [(CCNode*)target_ scale];
if(boundarySet)
{
CGPoint halfScreenSizeScaled = ccpMult(halfScreenSize, 1/s);
CGPoint fullScreenSizeScaled = ccpMult(fullScreenSize, 1/s);
CGPoint tempPos = ccpSub( halfScreenSizeScaled, followedNode_.position);

CGPoint posMin = ccpSub(fullScreenSizeScaled, ccp(rightBoundary,topBoundary));
CGPoint posMax = ccp(-leftBoundary,-bottomBoundary);
pos = ccp(clampf(tempPos.x,posMin.x,posMax.x), clampf(tempPos.y,posMin.y,posMax.y));
}
else
{
CGPoint halfScreenSizeScaled = ccpMult(halfScreenSize, 1/s);
pos = ccpSub( halfScreenSizeScaled, followedNode_.position );
}

pos.x *= s;
pos.y *= s;

if ( smoothingEnabled )
{
CGPoint oldPos = [target_ position];
CGPoint increment = ccpMult(ccpSub(pos,oldPos),smoothingFactor);
pos = ccpAdd(oldPos, increment);
}

[target_ setPosition:pos];
}

-(BOOL) isDone
{
return !followedNode_.isRunning;
}

-(void) stop
{
target_ = nil;
[super stop];
}

-(void) dealloc
{
[followedNode_ release];
[super dealloc];
}

@end

November 21, 2012 at 8:28 am #327961

Hexo
Participant
@hexo55

Hi! Here is the link to cocos2d+box2d project which tests the CCFollowDynamic class as requested by gatorsmile (I hope it’ll also be helpful to others). Take note that it uses cocos2d v2.0 stable and compiled in xcode 4.4.1.

PS. I have modified PhysicsSprite class to return the position of its physics body converted into points so that CCFollowDynamic can follow its actual position.

Link: http://www.mediafire.com/download.php?kl6jmj6p3git5zz

November 22, 2012 at 12:44 am #327962

Hexo
Participant
@hexo55

It seems the project I included doesn’t build fine (my bad for posting hastily).

Here is the updated link: http://www.mediafire.com/download.php?a46izip2i90trc6

November 22, 2012 at 12:54 am #327963

riq
Keymaster
@admin

@hexo55:

If smoothing factor is 0, then will it work like the orignal CCFollow ?

I would like to include this feature in cocos2d, but I don’t want to create a new class for it.

Do you think it would be possible to have both behaviors in just one class ? thanks.

Viewing 25 posts - 1 through 25 (of 34 total)

You must be logged in to reply to this topic.