Commit b7b2117b authored by Jonas Johan Solsvik's avatar Jonas Johan Solsvik 🎮
Browse files

Physics update successfully stops boxes from falling down

parent 983662da
#include <cstdio>
#include <cmath>
#include <iostream>
#include <SFML/Audio.hpp>
#include <SFML/Graphics.hpp>
#define FLOAT_MAX 3.40282347e+38F
using ThingID = uint;
struct Thing
{
ThingID id{};
sf::Vector2f velocity{0, 0};
sf::RectangleShape shape{};
};
using ThingVector = std::vector<Thing>;
using ThingIDVector = std::vector<uint>;
using ThingIDVector = std::vector<ThingID>;
auto eventsUpdate(
sf::RenderWindow &window,
......@@ -64,26 +68,27 @@ auto main() -> int
const auto PLAYER_SIZE = sf::Vector2f{50.0f, 50.0f};
const auto PLAYER_ORIGIN = sf::Vector2f{PLAYER_SIZE.x / 2.0f, PLAYER_SIZE.y / 2.0f};
Thing player1{};
Thing player1{0};
player1.shape.setFillColor(sf::Color::Red);
player1.shape.setSize(PLAYER_SIZE);
player1.shape.setOrigin(PLAYER_ORIGIN);
player1.shape.setPosition(sf::Vector2f{-100, -25});
things.emplace_back(player1);
player1.shape.setPosition(sf::Vector2f{-100, -500});
things.push_back(player1);
Thing player2{};
Thing player2{1};
player2.shape.setFillColor(sf::Color::Blue);
player2.shape.setSize(PLAYER_SIZE);
player2.shape.setOrigin(PLAYER_ORIGIN);
player2.shape.setPosition(sf::Vector2f{100, -25});
things.emplace_back(player2);
player2.shape.setPosition(sf::Vector2f{100, -500});
things.push_back(player2);
// # Create ground
Thing ground{};
Thing ground{2};
ground.shape.setSize(sf::Vector2f{4000, 20});
ground.shape.setPosition(-2000, 0);
ground.shape.setOrigin(2000, 10);
ground.shape.setPosition(0, 0);
ground.shape.setFillColor(sf::Color::White);
things.emplace_back(ground);
things.push_back(ground);
}
sf::Clock clock; // starts the clock
......@@ -131,9 +136,8 @@ auto physicsUpdate(
const ThingIDVector &dynamicThings,
const ThingIDVector &staticThings) -> void
{
const auto DRAG_COEFFISIENT = 0.1f;
const auto GRAVITY = 10.0f * 30;
const auto MAX_Y = 10.0f * 30;
const auto GRAVITY = 10.0f * 50;
const auto MAX_Y = 10.0f * 100;
const auto dt = deltaTime.asSeconds();
for (const auto dynamicThingID : dynamicThings)
......@@ -142,14 +146,13 @@ auto physicsUpdate(
// Add gravity to velocity
{
dynamicThing.velocity.y = std::min(dynamicThing.velocity.y + GRAVITY * dt, MAX_Y);
dynamicThing.velocity *= 1.0f - DRAG_COEFFISIENT * dt;
}
for (const auto otherDynamicThingID : dynamicThings)
{
if (dynamicThingID == otherDynamicThingID)
{
continue;
continue; //... do not test collision with self.
}
physicsTestCollision(dynamicThing, things[otherDynamicThingID], dt);
}
......@@ -158,8 +161,20 @@ auto physicsUpdate(
physicsTestCollision(dynamicThing, things[staticThing], dt);
}
}
std::cout << "----\n";
}
/**
* @function physicsTestCollision - Test collision between two rectangles (things)
* The algorithm is explained step-by-step by inline block comments below
*
* Assumptions:
* - All collisions happen between rectangles with no rotation.
* - All rectangles are NOT overlapping initially, and this
* function makes sure they will never overlap.
*
*/
auto physicsTestCollision(
Thing &collidingThing,
Thing &impactThing,
......@@ -167,7 +182,7 @@ auto physicsTestCollision(
{
const auto pos = collidingThing.shape.getPosition();
const auto dtPos = pos + collidingThing.velocity * dt;
const auto halfSize = collidingThing.shape.getSize() / 2.0f;
const auto halfSize = collidingThing.shape.getSize() * 0.5f;
/**
* Get the four corners of the colliding rectangle
......@@ -222,7 +237,7 @@ auto physicsTestCollision(
* -----| [impBotLeft] --------------- [impBotRight]
*/
const auto impPos = impactThing.shape.getPosition();
const auto impHalfSize = impactThing.shape.getSize() / 2.0f;
const auto impHalfSize = impactThing.shape.getSize() * 0.5f;
const auto impTopLeft = sf::Vector2f{impPos.x - impHalfSize.x, impPos.y - impHalfSize.y};
const auto impTopRight = sf::Vector2f{impPos.x + impHalfSize.x, impPos.y - impHalfSize.y};
......@@ -232,55 +247,92 @@ auto physicsTestCollision(
/**
* Now we are ready to test if any of the dt points are within the impacter
*
*
* |
* | [impTopLeft]---------------------------
* | |
* | |
* | |
* ---[botRight] ---------------|-------->> [dtBotRight] Inside?
* |
* |
* |
* [impBotLeft]--------------------------
* | [impTopLeft]---------------------------[impTopRight]
* | | |
* | | |
* | | |
* | | |
* ---[outsideCorner] ----------|-------->> [insideCorner?] |
* | |
* | |
* | |
* [impBotLeft]---------------------------- [impBotRight]
*
* If we find a point inside, we store two points in `outsideCorner` and `insideCorner`
*/
const bool insideBottomRight = dtBottomRight.x > impTopLeft.x &&
dtBottomRight.x < impTopRight.x &&
dtBottomRight.y > impTopLeft.y &&
dtBottomRight.y < impBottomLeft.y;
const bool insideBottomLeft = dtBottomLeft.x > impTopLeft.x &&
dtBottomLeft.x < impTopRight.x &&
dtBottomLeft.y > impTopLeft.y &&
dtBottomLeft.y < impBottomLeft.y;
const bool insideTopRight = dtTopRight.x > impTopLeft.x &&
dtTopRight.x < impTopRight.x &&
dtTopRight.y > impTopLeft.y &&
dtTopRight.y < impBottomLeft.y;
const bool insideTopLeft = dtTopLeft.x > impTopLeft.x &&
dtTopLeft.x < impTopRight.x &&
dtTopLeft.y > impTopLeft.y &&
dtTopLeft.y < impBottomLeft.y;
sf::Vector2f outsideCorner{};
sf::Vector2f insideCorner{};
if (dtBottomRight.x > impTopLeft.x &&
dtBottomRight.x < impTopRight.x &&
dtBottomRight.y > impTopLeft.y &&
dtBottomRight.y < impBottomLeft.y)
{
outsideCorner = bottomRight;
insideCorner = dtBottomRight;
}
else if (dtBottomLeft.x > impTopLeft.x &&
dtBottomLeft.x < impTopRight.x &&
dtBottomLeft.y > impTopLeft.y &&
dtBottomLeft.y < impBottomLeft.y)
{
outsideCorner = bottomLeft;
insideCorner = dtBottomLeft;
}
else if (dtTopRight.x > impTopLeft.x &&
dtTopRight.x < impTopRight.x &&
dtTopRight.y > impTopLeft.y &&
dtTopRight.y < impBottomLeft.y)
{
outsideCorner = topRight;
insideCorner = dtTopRight;
}
else if (dtTopLeft.x > impTopLeft.x &&
dtTopLeft.x < impTopRight.x &&
dtTopLeft.y > impTopLeft.y &&
dtTopLeft.y < impBottomLeft.y)
{
outsideCorner = topLeft;
insideCorner = dtTopLeft;
}
else
{
std::cout << "----OUTSIDE: "
<< collidingThing.id
<< " -> " << impactThing.id
<< " cy: " << collidingThing.shape.getPosition().y
<< " iy: " << impactThing.shape.getPosition().y
<< "\n";
return; // ...return early if no `insideCorner` found
}
/**
* Now we are ready to find the intersection point
*
* |
* | [D]---------------------------
* | |
* | \ | /
* | \|/
* ------[A] <<<----------------x---------->>> [B]
* Find intersection between insideCorner and all impactRectangle sides
*
* \ | /
* \|/
* ---------------[topIntersect]----------------
* | /|\ |
* \ | / | \ | /
* \|/ | \|/
* [leftIntersect]-------[insideCorner] ------[rightIntersect]
* /|\ | /|\
* / | \ | / | \
* | \|/ |
* ----------------[botIntersect]---------------|
* /|\
* / | \
* |
* [C]--------------------------
*
* @see https://www.tutorialspoint.com/program-for-point-of-intersection-of-two-lines-in-cplusplus
*
*/
const auto getIntersectionPoint = [](
const sf::Vector2f A,
const sf::Vector2f B,
const sf::Vector2f C,
const sf::Vector2f D) -> sf::Vector2f {
const auto intersection = [](
const sf::Vector2f A,
const sf::Vector2f B,
const sf::Vector2f C,
const sf::Vector2f D) -> sf::Vector2f {
// Line AB represented as a1x + b1y = c1
float a = B.y - A.y;
float b = A.x - B.x;
......@@ -305,30 +357,100 @@ auto physicsTestCollision(
return sf::Vector2f{x, y};
};
// Function to calculate distance
const auto leftIntersection = intersection(outsideCorner,
insideCorner,
impBottomLeft,
impTopLeft);
const auto bottomIntersection = intersection(outsideCorner,
insideCorner,
impBottomLeft,
impBottomRight);
const auto rightIntersection = intersection(outsideCorner,
insideCorner,
impBottomRight,
impTopRight);
const auto topIntersection = intersection(outsideCorner,
insideCorner,
impTopLeft,
impTopRight);
/**
* Calculate distance between all intersection points and the outsideCorner
*
* [outsideCorner]<-------------- distance ----------------->[topIntersect]
* |------------------------->[leftIntersect]
* |-------------------->[botIntersect]
* |-------------------------------->[rightIntersect]
*
* @returns distance as a floating point scalar value
*/
const auto distance = [](const sf::Vector2f A, const sf::Vector2f B) -> float {
// Calculating distance
return std::sqrt(std::pow(B.x - A.x, 2) +
std::pow(B.y - A.y, 2) * 1.0);
};
const float MIN = FLOAT_MAX;
if (insideBottomRight)
const auto leftD = distance(leftIntersection, outsideCorner);
const auto bottomD = distance(bottomIntersection, outsideCorner);
const auto rightD = distance(rightIntersection, outsideCorner);
const auto topD = distance(topIntersection, outsideCorner);
/**
* The colliding side is the side with the shortest distance to the
* outsideCorner.
*
* If colliding from LEFT or RIGHT, set x-velocity to 0
*
* |------------------|
* | |
* | |
* ---->>> [LEFT] [RIGHT] <<<-------
* | |
* | |
* |------------------|
*
*/
if (leftD < bottomD &&
leftD < rightD &&
leftD < topD)
{
// @see https://www.tutorialspoint.com/program-for-point-of-intersection-of-two-lines-in-cplusplus
const auto intersect = getIntersectionPoint(bottomRight,
dtBottomRight,
impBottomLeft,
impTopLeft);
std::cout << "----COLLIDING LEFT: " << collidingThing.id << " -> " << impactThing.id << "\n";
collidingThing.velocity.x = 0;
}
if (insideBottomLeft)
else if (rightD < bottomD &&
rightD < topD)
{
std::cout << "----COLLIDING RIGHT: " << collidingThing.id << " -> " << impactThing.id << "\n";
collidingThing.velocity.x = 0;
}
if (insideTopRight)
/*
* else if colliding from TOP or BOTTOM, set y-velocity to 0
*
* |
* |
* *
* |-------[TOP]--------|
* | |
* | |
* | |
* | |
* | |
* |-----[BOTTOM]-------|
* *
* |
* |
*/
else if (topD < bottomD)
{
std::cout << "----COLLIDING TOP: " << collidingThing.id << " -> " << impactThing.id << "\n";
collidingThing.velocity.y = 0;
}
if (insideTopLeft)
else // if (bottomD < topD)
{
std::cout << "----COLLIDING BOTTOM: " << collidingThing.id << " -> " << impactThing.id << "\n";
collidingThing.velocity.y = 0;
}
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment