views:

45

answers:

2

I have a sprite(image of a ball) I can move it using touch and move. I have also identified the rate of change of location(x-axis,y-axis) of that sprite depending on sliding period.

Now I need to continue that sprite to go according to its speed and direction. Here is my code-

Touch Event

- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {

  CGPoint location = [touch locationInView: [touch view]];
  CGPoint convertedLocation = [[CCDirector sharedDirector] convertToGL:location];

  self.touchStartTime = [event timestamp];
  self.touchStartPosition = location;

  if (YES == [self isItTouched:self.striker touchedAt:convertedLocation]) {
    self.striker.isInTouch = YES;
  }

  return YES;
}

- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event {

  CGPoint location = [touch locationInView: [touch view]];
  CGPoint convertedLocation = [[CCDirector sharedDirector] convertToGL:location];

  self.touchLastMovedTime = [event timestamp];
  self.touchMovedPosition = convertedLocation;


  if(self.striker.isInTouch == YES){
    self.striker.position = self.touchMovedPosition;
  }

}
- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event {

  CGPoint location = [touch locationInView: [touch view]];
  CGPoint convertedLocation = [[CCDirector sharedDirector] convertToGL:location];

  self.touchEndTime = [event timestamp];
  self.touchEndPosition = location;

  if( self.striker.isInTouch == YES 
    && ( self.touchEndTime - self.touchLastMovedTime ) <= MAX_TOUCH_HOLD_DURATION )
  {
    float c = sqrt( pow( self.touchStartPosition.x - self.touchEndPosition.x, 2 ) 
        + pow( self.touchStartPosition.y - self.touchEndPosition.y, 2 ) );

    self.striker.speedx =  ( c - ( self.touchStartPosition.y - self.touchEndPosition.y ) ) 
                         / ( ( self.touchEndTime - self.touchStartTime ) * 1000 );

    self.striker.speedy =  ( c - ( self.touchStartPosition.x - self.touchEndPosition.x ) ) 
             / ( ( self.touchEndTime -   self.touchStartTime ) * 1000 );

    self.striker.speedx *= 4;
    self.striker.speedy *= 4;

    self.striker.isInTouch = NO;
    [self schedule:@selector( nextFrame ) interval:0.001];

  }

}

Scheduled Method to move Sprite

- (void) nextFrame {

  [self setPieceNextPosition:self.striker];
  [self adjustPieceSpeed:self.striker];

  if( abs( self.striker.speedx ) <= 1 && abs( self.striker.speedy ) <= 1 ){
    [self unschedule:@selector( nextFrame )];
  }
}

SET next Position

- (void) setPieceNextPosition:(Piece *) piece{

  CGPoint nextPosition;
  float tempMod;
  tempMod = ( piece.position.x + piece.speedx ) / SCREEN_WIDTH;
  tempMod = (tempMod - (int)tempMod)*SCREEN_WIDTH;
  nextPosition.x = tempMod;

  tempMod = ( piece.position.y + piece.speedy ) / SCREEN_HEIGHT;
  tempMod = (tempMod - (int)tempMod)*SCREEN_HEIGHT;
  nextPosition.y = tempMod;

  piece.position = nextPosition;
}

Set new Speed

- (void) adjustPieceSpeed:(Piece *) piece{

  piece.speedx =(piece.speedx>0)? piece.speedx-0.05:piece.speedx+0.05;
  piece.speedy =(piece.speedy>0)? piece.speedy-0.05:piece.speedy+0.05;
}

Though, currently I am using static speed adjusting technique, but I hope to make it dynamic depending on initial speed( I appreciate any idea)

A: 

It looks like you have it pretty close. You're scheduling a selector on the touch end, computing the velocity, and moving the sprite according to that velocity until it damps out, so all the mechanical pieces are there.

What looks iffy is the physics. For instance, you're doing some odd things with the speed to slow the ball down which aren't going to look realistic at all.

What you want to do is find the "velocity vector" for the sprite when the finger is lifted. While you can attempt to do an instantaneous velocity for this, I've found that it works better to sample a few cycles back and average over them to get a final speed.

Then, once you've got that vector (it will look like an x and y speed, like you have now), you want to dampen the speed by doing something like this (untested pseudocode ahead):

 float speed = sqrt( speedx * speedx + speedy * speedy );
 if (speed == 0) {
     // kill motion here, unschedule or do whatever you need to
 }
 // Dampen the speed.
 float newSpeed = speed * .9;
 if (newSpeed < SOME_THRESHOLD) newSpeed = 0;
 speedx = speedx * newSpeed / speed;
 speedy = speedy * newSpeed / speed;
 //  Move sprite according to the new speed

This will keep the ball going in the same direction as when it was released, slowing down gradually until it stops. For more information, especially if you want to do some bouncing or anything, google for an introduction to vector algebra.

cc
Thanks @cc, i will try it soon and let you know.
Sadat
@cc if i am not wrong, this is similar to" speedx = speedx * 0.9; speedy = speedy * 0.9;"then why should i use newSpeed or those calculation?Please let me know if u have any other explanation.
Sadat
It's not different, except for the part where you want to know the current speed in order to damp the speed to explicitly zero. Typically, you'll want to do something special when you go from "not-zero speed" to "zero speed", so that's the framework I always use. In the example above, I'm just setting the speed to zero, but chances are, you're going to want to also unschedule that selector you're calling or set some other "at rest" flag. Of course, you could just detect that with a "if speedx < N and speedy < N" sort of thing, but that would give you (slightly) different results.
cc
A: 

thanks @all. I have got my solution as below-

//in adjustSpeed method
  piece.speedx -= piece.speedx * DEACCLERATION_FACTOR;
  piece.speedy -= piece.speedy * DEACCLERATION_FACTOR;

//and simply, free the sprite to move from ccTouchMoved method not ccTouchEnd
  if(self.striker.isInTouch == YES && distance_passed_from_touch_start>=a_threashold){
    //calculate speed and position here as it was in ccTouchEnd method
    self.striker.isInTouch = NO;
    [self schedule:@selector( nextFrame ) interval:0.001];

  }
Sadat