0
votes

I have a ball (CCSprite) that move from left to right forever. When I touch the screen, I want the ball stop. The problem is if the duration of the sprite is too fast, it has a delay time a millisecond before it stop, so the stop position is almost same value even when I touch in a bit 'different' position.

CGSize winSize = [[CCDirector sharedDirector] winSize];

self.ball = [CCSprite spriteWithFile:@"ball.png"];
self.ball.position = ccp(0, 156);

CGPoint destinationPoint = self.ball.position;
destinationPoint.x = winSize.width;

CGPoint startPoint = self.ball.position;
startPoint.x = 0;

float speed = 0.8f;

[self.ball runAction:[CCRepeatForever actionWithAction:
        [CCSequence actions:
            [CCMoveTo actionWithDuration:speed position:destinationPoint],
            [CCMoveTo actionWithDuration:0.f position:startPoint],
         nil]
        ]];

Then when I touch the screen I want to stop the sprite, and log the ball position:

-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
    [self.ball stopAllActions];
    NSLog(@"Ball x y: %f %f\n", self.ball.position.x, self.ball.position.y);
    // Then repeat again the movement of the ball
}

So when I touch at the center, it has a delay a millisecond time before it stopped. Once again, I mean when I touch, it delay millisecond time (the ball still moving) then it stop. For example, when I click at position x 240, it stopped at x 240,1122, when I click at position x 230, it stopped at 239,222. Not immediately stop after I touch.

Here is the sample log of the position I touch.

Ball x y: 240.211624 156.000000
Ball x y: 240.369995 156.000000
Ball x y: 240.291992 156.000000
Ball x y: 219.913574 156.000000
Ball x y: 260.425598 156.000000
Ball x y: 260.004364 156.000000
Ball x y: 239.953186 156.000000

See? The x position when stop is always 'can be divided by 20'.

  1. 240,240,240,239
  2. 219
  3. 260,260

I'm quite sure I have ever click at position x 235,230 or 245 or 250 or anything value exclude that pattern. But like I said, there is a delay time the ball still moving, then it stop.

If I change the speed become 2.8f (or higher) for example, the ball will stop immediately, and the x y of the ball is exact position in the last position when I touch. I think it is because the duration of CCMoveTo speed is too fast.

I have tried with schedule selector method. But the time of the schedule cannot be too fast as the CCMoveTo does. So the movement using schedule will be one pixel to one pixel with duration too long time.

Is there any solution for this? Or there is such a way to implement this? Many thanks!

2
I have tried your code. But i didn't get such pattern in stopped positions. I have used ccTouchesEnded instead of ccTouchEnded. - Anusha Kottiyal
@Anusha: it is same, using ccTouchesEnded or ccTouchEnded. If you have a time, maybe you can help me to test with this file. punyaku.com/cocos download TestLayer.h / TestLayer.m and ball.png / bg.png. - Jef
Try to stop the ball in center of the green line of background. The x position usually same. Ball x y: 199.955170 156.000000 Ball x y: 172.958817 156.000000 Ball x y: 173.278793 156.000000 Ball x y: 173.231598 156.000000 Ball x y: 173.510406 156.000000 Ball x y: 173.193588 156.000000 Ball x y: 173.302399 156.000000 Ball x y: 173.115173 156.000000 Ball x y: 186.736374 156.000000 Ball x y: 173.209991 156.000000 Ball x y: 186.598801 156.000000 Ball x y: 173.331207 156.000000 Ball x y: 186.952393 156.000000 The x is usually 173.xx or 186.xx even when I click between them. - Jef

2 Answers

0
votes

This is actually the expected behavior, and the effect is especially noticeable if you are animating the sprite at high speeds. Why?

An animation is essentially a change of a certain variable over time and this change is always carried out over a series of discrete time steps. Let's say your target distance is 240 pixels, and your desired duration is 0.8 seconds. Then your speed is 240*0.8 = 300 pixels/sec. Assuming a perfectly constant fps of 60 you would expect a discrete position change of 1/60* 300 = 5 pixels for every time step carried out.

Now let's say you receive a touch event at time t. More often than not, this t won't exactly coincide with the beginning of any timestep; it is more likely to lie in between some pair of time steps. Calling stopAllActions should stop scheduling the animation after the current timestep is done, it won't just stop in between two timesteps, so to speak. This is exactly why you are reporting distances that are always some interval apart.

Do you really need to be pixel-precise? if so, you will need to interpolate between positions at the two time frames to find the precise position.

Edit:

I actually highly doubt a pixel-precise solution is required though in this case. In fact I suspect the perceived mili-second delay could be due to the detection of the touch event when it ends instead of when it begins. You might want to try capturing that event in ccTouchBegan instead.

0
votes

Your second CCMoveTo action is unnecessary - you don't want to perform a move animation of 0 seconds, you want to instantly move it back to the start. I would recommend changing your animation to the following:

[self.ball runAction:[CCRepeatForever actionWithAction:
    [CCSequence actions:
        [CCMoveTo actionWithDuration:speed position:destinationPoint],
        [CCCallBlock actionWithBlock:^
        {
            self.ball.position = ccp(0, 156);
        }],
     nil]
    ]];

EDIT: Apologies - I did not understand the question (but still suggest the change). Interpolation is what you are looking for.

You can set a timer at the beginning of the animation and reset it when the ball resets position. Given the known duration the ball will move across the screen and the width of the screen, you can calculate the ball's expected position at a given time.