Search code examples
cocos2d-iphonegame-physics

Cocos2d Bow-Arrow movement with air friction and no physics engine, How?


I've spent the last 2 days figuring out the movement of an arrow shot from a bow with no perfect end result. Here is what I have so far:

    - (void) update:(ccTime) dt {
    elapsedTime += dt;
    CGFloat t = elapsedTime;
    float theta = CC_DEGREES_TO_RADIANS(angle);
    velocity = ccp (initialVelocity.x* cos(theta), initialVelocity.y*sin(theta));


    float k = 0.3; //this would be the air resistance factor
    float vvx = sqrtf( velocity.x*velocity.x + velocity.y*velocity.y );
    float vvy = sqrtf( velocity.x*velocity.x + velocity.y*velocity.y );

    CGFloat ax = -k*vvx; //accelerationX
    CGFloat ay = -k*vvy - gravity; //accelerationY


    velocity = ccp(velocity.x + ax * t , velocity.y + ay * t);

    CGPoint oldPosition = self.position;
    CGPoint newPosition = ccp(self.position.x + velocity.x * t + ax * t * t, self.position.y + velocity.y * t + ay * t * t);

    CGPoint v1 = CGPointMake(0.0, 0.0);
    CGPoint v2 = CGPointMake(newPosition.x - oldPosition.x ,  newPosition.y - oldPosition.y);
   CGFloat newAngle = (atan2(v2.y, v2.x) - atan2(v1.y, v1.x));
    self.rotation = CC_RADIANS_TO_DEGREES(-newAngle);
    self.position = newPosition;

}

Using this code I get this behaviour:

Using: k = 0.3 and angle = 0 degrees, gravity = 9.8

With initialVelocity = 100 the arrow has a nice parabola trajectory

With initialVelocity = 200 the arrow moves faster but has the exact same trajectory as with initialVelocity = 100

With initialVelocity = 300 the arrow moves a lot faster and the trajectory is slightly different but still very close to the trajectory of initialVelocity = 100

Is something wrong with my code? Please note that I don't have a very good understanding of all the notions, much of the implementation is a hit & miss based on what I've read online.


Solution

  • You're adding in acceleration multiple times. You have:

    velocity = ccp(velocity.x + ax * t , velocity.y + ay * t);
    

    and then below that:

    CGPoint newPosition = ccp(self.position.x + velocity.x * t + ax * t * t, self.position.y + velocity.y * t + ay * t * t);
    

    It looks to me like you could simplify that second line to just

    CGPoint newPosition = ccp(self.position.x + velocity.x * t, self.position.y + velocity.y * t);
    

    However, I think your overall algorithm is more complicated than it needs to be, and that makes bugs harder to find. There shouldn't be any reason to track angle; just separate the vectors and handle each independently. In pseudocode, something like this:

    // setup
    vx = cos(InitAngle) * InitVelocity;
    vy = sin(InitAngle) * InitVelocity;
    locx = 0;
    locy = 0;
    gravity = -1.0;
    friction  -0.01;
    
    // loop
    Update(t) {
      vx *= 1+(friction * t);
      vy +=gravity * t;
      locx += vx * t;
      locy += vy * t;
    }
    

    (I may have the sin/cos reversed; I always get that wrong.)