void ribi::Boenken::SpriteMoving::Collision(SpriteMoving * const p1, SpriteMoving * const p2) { assert(p1!=p2); assert(p1 > p2); const double dx = p2->getX() - p1->getX(); const double dy = p2->getY() - p1->getY(); const double distance = std::sqrt((dy * dy) + (dx * dx)); const double collision_distance = boost::numeric_cast<double>(p1->m_size + p2->m_size) / 2.0; if (distance < collision_distance) { //A collision! //Obtain the relative angle between the players const double a = GetAngle(dx,dy); //Obtain the players' current impulses double p1_a = p1->CalcImpulseAngle(); double p1_s = p1->CalcImpulseSpeed(); double p2_a = p2->CalcImpulseAngle(); double p2_s = p2->CalcImpulseSpeed(); //Obtain the new impulses DoPerfectElasticCollision(a, p1_a,p1_s,p2_a,p2_s); //Set the players' new impulses const double dx1 = std::sin(p1_a) * p1_s; const double dy1 = -std::cos(p1_a) * p1_s; const double dx2 = std::sin(p2_a) * p2_s; const double dy2 = -std::cos(p2_a) * p2_s; p1->SetSpeed(dx1,dy1); p2->SetSpeed(dx2,dy2); //std::clog << "New impulses: (" << dx1 << "," << dy1 << ")" // << " (" << dx2 << "," << dy2 << ")\n"; //Let them move away from each perpendicalar to the collision axis { const double go_away_distance = collision_distance - distance; assert(go_away_distance > 0); const double pi = boost::math::constants::pi<double>(); const double go_away_dx1 = std::sin(a + pi) * (go_away_distance / 2.0); const double go_away_dy1 = -std::cos(a + pi) * (go_away_distance / 2.0); const double go_away_dx2 = std::sin(a + 0.0) * (go_away_distance / 2.0); const double go_away_dy2 = -std::cos(a + 0.0) * (go_away_distance / 2.0); p1->Move(go_away_dx1,go_away_dy1); p2->Move(go_away_dx2,go_away_dy2); } //Let the players move again p1->Move(); p2->Move(); #ifndef NDEBUG { const double new_dx = p2->getX() - p1->getX(); const double new_dy = p2->getY() - p1->getY(); const double new_distance = std::sqrt((new_dy * new_dy) + (new_dx * new_dx)); if (new_distance < distance) { //std::clog << "Players should in general move away after a collision\n"; } //assert(new_distance > distance && "Players should move away after a collision"); } #endif } }
void ribi::bnkn::SpriteNonMoving::Collision( const SpriteNonMoving& obstacle, SpriteMoving& moving ) { if (!IsCollision(obstacle,moving)) return; /* O - | | dy (in this case > 0) | M - |---| dx (in this case > 0) */ const double dx = moving.getX() - obstacle.getX(); const double dy = moving.getY() - obstacle.getY(); //const double collision_distance // = boost::numeric_cast<double>(obstacle.m_size + moving.m_size) / 2.0; //Obtain the relative angle between the players /* O \ \ \ M angle_players in this case 135 degrees */ const double angle_players = Geometry().GetAngleClockScreen(dx,dy); //Obtain the moving sprite's current impulse double moving_angle = moving.CalcImpulseAngle(); double moving_speed = moving.CalcImpulseSpeed(); //Obstacles have opposite impulse const double pi = boost::math::constants::pi<double>(); double obstacle_angle = moving_angle + pi; double obstancle_speed = moving_speed; //Obtain the new impulses DoPerfectElasticCollision( angle_players, obstacle_angle,obstancle_speed,moving_angle,moving_speed ); //Set the player's new impulse const double dx2 = std::sin(moving_angle) * moving_speed; const double dy2 = -std::cos(moving_angle) * moving_speed; moving.SetSpeed(dx2,dy2); //Let the player move again moving.Move(); }
void advance(int phase) { if (phase == 0) { //Bounce against others QList<QGraphicsItem*> others = this->collidingItems(); const int n_others = others.size(); for (int i=0; i!=n_others; ++i) { SirSprite * const other = dynamic_cast<SirSprite*>(others[i]); if (!other) continue; if (other == this) continue; //Ensure checking is only done once per colliding pair if (this->zValue() < other->zValue()) continue; //Relative between players 1 and 2 const double dx_between = other->x() - this->x(); const double dy_between = other->y() - this->y(); const double angle_between = GetAngle(dx_between,dy_between); //For this player const double this_dx = std::cos(this->angle) * this->speed; const double this_dy = -std::sin(this->angle) * this->speed; double this_angle = GetAngle( this_dx, this_dy); double this_speed = std::sqrt((this_dy * this_dy) + (this_dx * this_dx)); //For other player const double other_dx = std::cos(other->angle) * other->speed; const double other_dy = -std::sin(other->angle) * other->speed; double other_angle = GetAngle( other_dx, other_dy); double other_speed = std::sqrt((other_dy * other_dy) + (other_dx * other_dx)); DoPerfectElasticCollision( angle_between, this_angle, this_speed, other_angle, other_speed); this->angle = this_angle; this->speed = this_speed; other->angle = other_angle; other->speed = other_speed; //Let them move once setX(x() + (std::sin(angle) * speed)); setY(y() - (std::cos(angle) * speed)); other->setX(other->x() + (std::sin(other->angle) * other->speed)); other->setY(other->y() - (std::cos(other->angle) * other->speed)); //Infection? if (this->state == infected && other->state == susceptible) { other->setState(infected); } if (other->state == infected && this->state == susceptible) { this->setState(infected); } } } //Bounce against the edges else if (phase == 1) { while (1) { #ifdef __STRICT_ANSI__ const double pi = boost::math::constants::pi<double>(); #else const double pi = M_PI; #endif setX(x() + (std::sin(angle) * speed)); setY(y() - (std::cos(angle) * speed)); if (x() < 0.0) { setX(x()+1); angle = (0.0*pi) + ((0.0*pi) - angle); continue; } if (y() < 0.0) { setY(y()+1); angle = (0.5*pi) + ((0.5*pi) - angle); continue; } if (x() > maxx) { setX(x()-1); angle = (1.0*pi) + ((1.0*pi) - angle); continue; } if (y() > maxy) { setY(y()-1); angle = (1.5*pi) + ((1.5*pi) - angle); continue; } break; } //Resistance? if (state == infected && timer.elapsed() > 5.0) setState(resistant); } }