bool Board::find(cv::Point const& target, Ball::PtrList & myballs) const { for(auto bb = balls.rbegin(); bb!=balls.rend();++bb) { for(auto b : *bb) { if(b->disable == true) continue ; if(circlesColliding(b->point.x, b->point.y, radius*0.7, target.x, target.y, radius*0.8)) { myballs.push_back(b); } } } return myballs.size() > 0; }
// TODO: split this beast up into sub functions! // returns true if there is a collision occuring between the two entities. bool SCollision::collisionOccurring(EntityManager * entM, PPosition* pos1, PBoundingBox* ent1, PPosition* pos2, PBoundingBox* ent2) { VECTOR2 collisionVector; collisionVector.y = 0.0f; collisionVector.x = 0.0f; int count = 0; if(ent1->collisionType == PBoundingBoxNS::LINE && ent2->collisionType == PBoundingBoxNS::CIRCLE) { PGravity * gobj = entM->getProperty<PGravity>(ent2->entity_id); PMobile * mob = entM->getProperty<PMobile>(ent2->entity_id); // store the entity's current position in temporary variables. we will be checking collision with the entity's // upcoming position that has yet to be computed for this frame (the SMovement system computes new entity positions // after the SCollision system resolves collisions). float tempX = pos2->x, tempY = pos2->y; if ( mob != nullptr ) { // determine the entity's upcoming position for this frame, and resolve collision // with this new position pos2->x = pos2->x + mob->direction.x*mob->velocity.x*_Game->getDeltaTime(); pos2->y = pos2->y + mob->direction.y*mob->velocity.y*_Game->getDeltaTime(); if( gobj != nullptr && !gobj->grounded && lineCircleColliding(pos1, ent1, pos2, ent2, collisionVector)) { // reset y position pos2->y = tempY; /* if collisionVector.y != 0, then this line is representative of the "ground" or "floor". collisionVector.y == 0 only if the line's rise is greater than its run, in which case we treat the line as an impassable "wall" and restrict the colliding entity's movement in the x-direction with which it is colliding with the wall. additionally, we only resolve collisions with the ground when falling (velocity.y >= 0) */ if ( collisionVector.y < 0.0f && mob->velocity.y >= 0 ) { /* Determine how much of the radius is factored into shifting the entity upwards away from the line that is being collided with. (In other words, find the y position of the circle tangent to the line being collided with) For one extreme case, when the slope is zero, the entirety of the entity's radius is subtracted from the y point of collision on the line (a line with slope zero is tangent to the bottom (or top) of the circle. So we can simply do y_collision_point - center of entity - radius to determine the dy with which to move our entity as to position it so that its circle hitbox is tangent to the line.) On the other hand, if the slope were infinite instead of zero (a vertical line), then none of the entity's radius is subtracted from the y point of collision, because in this case the line is tangent to the left or right of the circle (the horizontal radius is perpendicular to the line), and so we do not adjust the entity's y position at all (as adjusting the y position does nothing to separate these colliding entities if the line intersecting the circle is vertical). For lines with intermediate slopes, we can multiply the radius by the cosine of the triangle whose hypotenuse is the line being collided with to determine how far we can shift the entity so that its comprised circle is tangent to the line. */ float slope = ent1->line.B.y / ent1->line.B.x; float magnitude = sqrt( ent1->line.B.x*ent1->line.B.x + ent1->line.B.y*ent1->line.B.y); // cosine of the triangle whose hypotenuse is the line float cosine = ent1->line.B.x/magnitude; // sine of the triangle whose hypotenuse is the line float sine = -ent1->line.B.y/magnitude; // determine the y point of collision on the line float y = slope*( pos2->getCenterX() + ent2->radius*sine - pos1->x ) + pos1->y; // subtract the y point of collision from the entity's center, minus the y component of the vector // perpendicular to the line ( whose magnitude is the circle's radius ). // to the line float dy = ( y - pos2->getCenterY() ) - ent2->radius*cosine; // reset the entity's x position ( we reset it here because we needed to use the upcoming x position // in the above computation of y ) pos2->x = tempX; SMovement::moveEntity(pos2, 0, dy); // set the entity's gravity property's grounded field to true so that the SGravity system // knows not to "pull down" on the entity. gobj->grounded = true; // set this entity's downward velocity to zero. // set this entity's downward velocity to zero. mob->velocity.y = 0.0f; } else if ( collisionVector.x != 0.0f ) // if colliding with a vertical line { // reset entity's x position after checking collision of future position pos2->x = tempX; // if entity is to the right of the vertical line, shift factor is the colliding entity's radius. // otherwise, the shift factor is the negative of this radius. float shift = collisionVector.x == -1.0f ? ent2->radius*-1 - 2.0f : ent2->radius + 2.0f; float dx = pos1->x - pos2->getCenterX() + shift; SMovement::moveEntity(pos2, dx, 0); mob->velocity.x = 0; } return true; } pos2->x = tempX; pos2->y = tempY; } return false; } else if(ent1->collisionType == PBoundingBoxNS::CIRCLE && ent2->collisionType == PBoundingBoxNS::LINE) { PGravity * gobj = entM->getProperty<PGravity>(ent1->entity_id); PMobile * mob = entM->getProperty<PMobile>(ent1->entity_id); float tempX = pos1->x, tempY = pos1->y; if ( mob != nullptr ) { // determine the entity's upcoming position for this frame, and resolve collision // with this new position pos1->x = pos1->x + mob->direction.x*mob->velocity.x*_Game->getDeltaTime(); pos1->y = pos1->y + mob->direction.y*mob->velocity.y*_Game->getDeltaTime(); if( gobj != nullptr && !gobj->grounded && lineCircleColliding(pos2, ent2, pos1, ent1, collisionVector)) { // reset y position pos1->y = tempY; if ( collisionVector.y < 0.0f && mob->velocity.y >= 0 ) { float slope = ent2->line.B.y / ent2->line.B.x; float magnitude = sqrt( ent2->line.B.x*ent2->line.B.x + ent2->line.B.y*ent2->line.B.y); // cosine of the triangle whose hypotenuse is the line float cosine = ent2->line.B.x/magnitude; // sine of the triangle whose hypotenuse is the line float sine = -ent2->line.B.y/magnitude; // determine the y point of collision on the line float y = slope*( pos1->getCenterX() + ent1->radius*sine - pos2->x ) + pos2->y; float dy = y - pos1->getCenterY() - ent1->radius*cosine; pos1->x = tempX; SMovement::moveEntity(pos1, 0, dy); // set the entity's gravity property's grounded field to true so that the SGravity system // knows not to "pull down" on the entity. gobj->grounded = true; // set this entity's downward velocity to zero. mob->velocity.y = 0.0f; } else if ( collisionVector.x != 0.0f ) // if colliding with a vertical line { pos1->x = tempX; float shift = collisionVector.x == -1.0f ? ent1->radius*-1 - 2.0f : ent1->radius + 2.0f; float dx = pos2->x - pos1->getCenterX() + shift; SMovement::moveEntity(pos1, dx, 0); mob->velocity.x = 0; } return true; } pos1->x = tempX; pos1->y = tempY; } return false; } else if(ent1->collisionType == PBoundingBoxNS::CIRCLE && ent2->collisionType == PBoundingBoxNS::BOX) { float x1 = 0.0f, x2= 0.0f, y1= 0.0f, y2= 0.0f; while((ent1->deflect || ent2->deflect) && count < 10 && boxCircleColliding(pos2, ent2, pos1, ent1, collisionVector)) { if(ent2->deflect) { y1 += collisionVector.y; x1 += collisionVector.x; } if(ent1->deflect) { y2 -= collisionVector.y; x2 -= collisionVector.x; } count++; } SMovement::moveEntity(pos1, x1, y1); SMovement::moveEntity(pos2, x2, y2); if(count == 0) { return boxCircleColliding(pos2, ent2, pos1, ent1, collisionVector); } return true; } else if(ent1->collisionType == PBoundingBoxNS::BOX && ent2->collisionType == PBoundingBoxNS::CIRCLE) { float x1 = 0.0f, x2= 0.0f, y1= 0.0f, y2= 0.0f; while((ent1->deflect || ent2->deflect) && count < 10 && boxCircleColliding(pos1, ent1, pos2, ent2, collisionVector)) { if(ent2->deflect) { y1 += collisionVector.y; x1 += collisionVector.x; } if(ent1->deflect) { y2 -= collisionVector.y; x2 -= collisionVector.x; } count++; } SMovement::moveEntity(pos1, x1, y1); SMovement::moveEntity(pos2, x2, y2); if(count == 0) { return boxCircleColliding(pos1, ent1, pos2, ent2, collisionVector); } return true; } else if(ent1->collisionType == PBoundingBoxNS::CIRCLE && ent2->collisionType == PBoundingBoxNS::CIRCLE) { float x1 = 0.0f, x2= 0.0f, y1= 0.0f, y2= 0.0f; while((ent1->deflect || ent2->deflect) && count < 10 && circlesColliding(pos1, ent1, pos2, ent2, collisionVector)) { Graphics::Vector2Normalize(&collisionVector); if(ent2->deflect) { y1 += collisionVector.y; x1 += collisionVector.x; } if(ent1->deflect) { y2 -= collisionVector.y; x2 -= collisionVector.x; } count++; } SMovement::moveEntity(pos1, x1, y1); SMovement::moveEntity(pos2, x2, y2); if( count == 0 ) { return circlesColliding(pos1, ent1, pos2, ent2, collisionVector); } return false; } else if(ent1->collisionType == PBoundingBoxNS::BOX) { while(count < 10 && (ent1->deflect || ent2->deflect) && boxesColliding(pos1, ent1, pos2, ent2, collisionVector)) { if(ent2->deflect) SMovement::moveEntity(pos1, collisionVector.x, collisionVector.y); if(ent1->deflect) SMovement::moveEntity(pos2, -collisionVector.x, -collisionVector.y); count++; } if(count == 0) return false; return true; } return false; }