void ofApp::setupScene(int width, int height) { /* setup camera */ double aspectRatio = static_cast<double>(width) / height; ofVec3f topLeft (-CAMERA_SIZE * aspectRatio, CAMERA_SIZE, 0.0); ofVec3f botLeft (-CAMERA_SIZE * aspectRatio, -CAMERA_SIZE, 0.0); ofVec3f botRight( CAMERA_SIZE * aspectRatio, -CAMERA_SIZE, 0.0); cam = OffAxisCamera(topLeft, botLeft, botRight); cam.setPosition(ofVec3f(0.0, 0.0, 50.0f)); /* calculate grid box dimension */ ofVec3f camWidthVec = botRight - botLeft; double camWidth = camWidthVec.length(); gridBox = GridBox(ofVec3f(-CAMERA_SIZE * aspectRatio, -CAMERA_SIZE, 0.0f), camWidth, camWidth / aspectRatio, 8.0f); }
MTV Collision::getCollision(const sf::Sprite& object1,Entity::ENTITY_SHAPE shape1,const sf::Sprite& object2,Entity::ENTITY_SHAPE shape2){ //Use Separating Axis Theorem to determine whether two objects are overlapping //See documentation for full explanation of this algorithm //Get the oriented bounding box in world coordinates (includes rotation) of objects OBB obb1 = getOBB(object1); OBB obb2 = getOBB(object2); float rot = object1.getRotation(); double overlap = LONG_MAX; maths::Vector2 smallest(0,0); std::vector<maths::Vector2> axis1; std::vector<maths::Vector2> axis2; maths::Vector2 circleCentre1; maths::Vector2 circleCentre2; sf::FloatRect gbounds1 = object1.getGlobalBounds(); sf::FloatRect gbounds2 = object2.getGlobalBounds(); //Find all the axes to check using SAT based on shapes of objects //Works with squares/rectangles or circles //Rectangles only need 2 axes because they have 2 sets of parallel lines if(shape1 == Entity::SHAPE_CIRCLE && shape2 == Entity::SHAPE_CIRCLE){ //If both shapes are circles, the only axis needed is the axis between centres of circles circleCentre1 = maths::Vector2(gbounds1.left + gbounds1.width / 2, gbounds1.top + gbounds1.height / 2); circleCentre2 = maths::Vector2(gbounds2.left + gbounds2.width / 2, gbounds2.top + gbounds2.height / 2); axis1.push_back(maths::Vector2(circleCentre1 - circleCentre2).normalise()); }else if(shape1 != shape2){ //if one shape is circle and one shape is rectangle maths::Vector2 circleCentre; sf::FloatRect squareRect; float rotation; //First get the unrotated bounding box and centre of the circle of the 2 objects if(shape1 == Entity::SHAPE_CIRCLE){ circleCentre = maths::Vector2(gbounds1.left + gbounds1.width / 2, gbounds1.top + gbounds1.height / 2); circleCentre1 = circleCentre; squareRect = getOriginalBoundingBox(object2); rotation = object2.getRotation(); }else if(shape2 == Entity::SHAPE_CIRCLE){ circleCentre = maths::Vector2(gbounds2.left + gbounds2.width / 2, gbounds2.top + gbounds2.height / 2); circleCentre2 = circleCentre; squareRect = getOriginalBoundingBox(object1); rotation = object1.getRotation(); } maths::Vector2 squareCentre(squareRect.left + squareRect.width / 2, squareRect.top + squareRect.height / 2); OBB* square = (shape1 == Entity::SHAPE_SQUARE ? &obb1 : &obb2); maths::Vector2 relativeCircleCentre = circleCentre.rotate(-rotation, squareCentre); //get circle centre in relation to the rotated square bool vertice = false; maths::Vector2 axis; maths::Vector2 topLeft(squareRect.left, squareRect.top); maths::Vector2 topRight(squareRect.left + squareRect.width, squareRect.top); maths::Vector2 botLeft(squareRect.left, squareRect.top + squareRect.height); maths::Vector2 botRight(squareRect.left + squareRect.width, squareRect.top + squareRect.height); //Get the closest vertex of the rectangle to the circle centre. //The axis to check is the vector between these 2 points. if(circleCentre.x < topLeft.x){ if(circleCentre.y < topLeft.y){ vertice = true; axis = topLeft; }else if(circleCentre.y > botLeft.y){ vertice = true; axis = botLeft; }else{ axis = maths::Vector2(topLeft - botLeft).normalise(); } }else if(circleCentre.x > topRight.x){ if(circleCentre.y < topLeft.y){ vertice = true; axis = topRight; }else if(circleCentre.y > botLeft.y){ vertice = true; axis = botRight; }else{ axis = maths::Vector2(topRight - botRight).normalise(); } }else{ if(circleCentre.y < topLeft.y){ axis = maths::Vector2(topRight - topLeft).normalise(); }else if(circleCentre.y > botLeft.y){ axis = maths::Vector2(botLeft - botRight).normalise(); }else{ //contains point! } } if(vertice){ axis1.push_back(maths::Vector2(circleCentre - axis).normalise()); }else{ axis1.push_back(maths::Vector2(topRight - topLeft).normalise()); axis1.push_back(maths::Vector2(topRight - botRight).normalise()); } }else{ //If both shapes are rectangles //Get vectors for sides of shapes maths::Vector2 Xside1(obb1.bot_left - obb1.bot_right); maths::Vector2 Yside1(obb1.top_left - obb1.bot_left); maths::Vector2 Xside2(obb2.bot_left - obb2.bot_right); maths::Vector2 Yside2(obb2.top_left - obb2.bot_left); //Axes requires are perpendicular to the sides of the shape //Vector2.perpendicular() normalises for greater accuracy axis1.push_back(Xside1.perpendicular()); axis1.push_back(Yside1.perpendicular()); axis2.push_back(Xside2.perpendicular()); axis2.push_back(Yside2.perpendicular()); } //We have all the axes to check. //Now find details on collisions with projections. for(int i=0;i<axis1.size();i++){ //Get projection of axis for both shapes maths::Vector2 axis = axis1[i]; Projection projection1 = project(obb1, axis); if(shape1 == Entity::SHAPE_CIRCLE){ float radius = gbounds1.width / 2; projection1 = projectCircle(circleCentre1, radius, axis); } Projection projection2 = project(obb2, axis); if (shape2 == Entity::SHAPE_CIRCLE){ float radius = gbounds2.width / 2; projection2 = projectCircle(circleCentre2, radius, axis); } //If a projection does not overlap, we know the objects do not collide so we can exit the function. //Otherwise, the MTV (minimum translation vector required to make the objects not collide) is calculated. if(!projection1.overlap(projection2)){ return MTV::NONE; }else{ //The axis with the smallest overlap is the axis used to calculate MTV, so record it. double o = projection1.getOverlap(projection2); if(o < overlap){ overlap = o; //set smallest overlap smallest = axis; //set smallest separation vector } } } //Repeat the same process as above with the other set of axes. for(int i=0;i<axis2.size();i++){ maths::Vector2 axis = axis2[i]; Projection projection1 = project(obb1, axis); if(shape1 == Entity::SHAPE_CIRCLE){ float radius = gbounds1.width/2; projection1 = projectCircle(circleCentre1, radius, axis); } Projection projection2 = project(obb2,axis); if(shape2 == Entity::SHAPE_CIRCLE){ float radius = gbounds2.width / 2; projection2 = projectCircle(circleCentre2, radius, axis); } if(!projection1.overlap(projection2)){ return MTV::NONE; }else{ double o = projection1.getOverlap(projection2); if(o < overlap){ overlap = o; smallest = axis; } } } //Get the vector from the centre of object 2 to the centre of object 1 maths::Vector2 centre1 = maths::Vector2(gbounds1.left + gbounds1.width / 2, gbounds1.top + gbounds1.height / 2); maths::Vector2 centre2 = maths::Vector2(gbounds2.left + gbounds2.width / 2, gbounds2.top + gbounds2.height / 2); maths::Vector2 between = centre1 - centre2; //If the separation vector is in the opposite direction of 'between', flip it round by negating it if(between.dot(smallest) < 0){ smallest = -smallest; } MTV mtv(overlap, smallest); return mtv; }