1
General / The problem with collision
« on: November 24, 2024, 05:24:07 pm »
I am trying to implement collision detection from the video by javidx9, "Arbitrary Rectangle Collision Detection & Resolution - Complete," but sometimes, when one object touches another, the first object flies off unexpectedly. This might be related to velocity.y = 0, but I’m not sure about the exact cause. I also discovered that in the DynamicRectVsRect function, when multiplying in_rect_vel.x by fElapsedTime (where in_rect_vel.x == 0), it becomes a very small number instead of zero.
Code:
Code:
#include <iostream>
#include <SFML/Graphics.hpp>
bool RayVsRect(sf::Vector2f ray_origin, sf::Vector2f ray_direction, sf::RectangleShape target, sf::Vector2f& contact_point, sf::Vector2f& contact_normal, float& t_hit_time);
bool DynamicRectVsRect(sf::RectangleShape& in, sf::RectangleShape target, sf::Vector2f in_rect_vel, sf::Vector2f& contact_point, sf::Vector2f& contact_normal, float& contact_time, float fElapsedTime);
sf::Vector2f normalize(sf::Vector2f vector);
int main()
{
sf::RenderWindow window(sf::VideoMode(800, 600), "My window");
window.setFramerateLimit(60);
std::vector<sf::RectangleShape> vRects;
vRects.push_back(sf::RectangleShape());
vRects[0].setPosition({10.0f, 10.0f});
vRects[0].setSize({30.0f, 20.0f});
sf::Vector2f velocity = {0, 0};
vRects.push_back(sf::RectangleShape());
vRects[1].setPosition({ 100.0f, 100.0f });
vRects[1].setSize({ 80.0f, 50.0f });
sf::RectangleShape sfml_rect;
sfml_rect.setPosition({100.0, 100.0});
sfml_rect.setSize({ 200.0, 200.0 });
sf::CircleShape circle;
int circle_rad = 5;
circle.setFillColor(sf::Color::Red);
circle.setRadius(circle_rad);
sf::Vector2f ray_point = {20.0f, 20.0f};
sf::Vector2f ray_direction;
sf::Vector2i MousePos;
sf::Vector2f cp, cn;
float ct;
float t;
sf::Vertex normal[2];
sf::Clock clock;
float deltaTime;
while (window.isOpen())
{
MousePos = sf::Mouse::getPosition(window);
ray_point = sf::Vector2f(vRects[0].getPosition().x+vRects[0].getSize().x/2, vRects[0].getPosition().y + vRects[0].getSize().y / 2);
ray_direction = sf::Vector2f(MousePos.x - ray_point.x, MousePos.y - ray_point.y);
deltaTime = clock.restart().asSeconds();
//std::cout << ray_direction.x << '\t' << ray_direction.y << std::endl;
if (sf::Mouse::isButtonPressed(sf::Mouse::Left)) {
sf::Vector2f norm_ray_direction = normalize(ray_direction);
velocity += sf::Vector2f(norm_ray_direction.x * 100.0f * deltaTime, norm_ray_direction.y * 100.0f * deltaTime);
}
//std::cout << velocity.x << '\t' << velocity.y << std::endl;
for (int i = 1; i < vRects.size(); i++) {
if (DynamicRectVsRect(vRects[0], vRects[i], velocity, cp, cn, ct, deltaTime)) {
velocity += sf::Vector2f(cn.x * std::abs(velocity.x) * (1-ct), cn.y * std::abs(velocity.y) * (1-ct));
}
}
vRects[0].move(velocity.x * deltaTime, velocity.y * deltaTime);
std::cout << velocity.x << '\t' << velocity.y << "\t\t" << vRects[0].getPosition().x << '\t' << vRects[0].getPosition().x << std::endl;
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
window.close();
}
}
window.clear(sf::Color::Black);
for (int i = 0; i < vRects.size(); i++) {
window.draw(vRects[i]);
}
window.display();
}
return 0;
}
bool RayVsRect(sf::Vector2f ray_origin, sf::Vector2f ray_direction, sf::RectangleShape target, sf::Vector2f& contact_point, sf::Vector2f& contact_normal, float& t_hit_near) {
sf::Vector2f t_near = sf::Vector2f((target.getPosition().x - ray_origin.x) / ray_direction.x,
(target.getPosition().y - ray_origin.y) / ray_direction.y);
sf::Vector2f t_far = sf::Vector2f((target.getPosition().x + target.getSize().x - ray_origin.x) / ray_direction.x,
(target.getPosition().y + target.getSize().y - ray_origin.y) / ray_direction.y);
std::cout << "ray_direction: " << ray_direction.x << '\t' << ray_direction.y << std::endl;
std::cout << "t_near: " << t_near.x << '\t' << t_near.y << std::endl;
std::cout << "t_far: " << t_far.x << '\t' << t_far.y << std::endl;
if (t_near.x > t_far.x) std::swap(t_near.x, t_far.x);
if (t_near.y > t_far.y) std::swap(t_near.y, t_far.y);
if (t_near.x > t_far.y || t_near.y > t_far.x) return false;
//std::cout << "t_near: " << t_near.x << '\t' << t_near.y << std::endl;
t_hit_near = std::max(t_near.x, t_near.y);
float t_hit_far = std::min(t_far.x, t_far.y);
if (t_hit_far < 0) return false;
contact_point = ray_origin + t_hit_near * ray_direction;
if (t_near.x > t_near.y)
{
if (ray_direction.x < 0)
{
contact_normal = { 1, 0 };
}
else
{
contact_normal = { -1, 0 };
}
}
else if (t_near.x < t_near.y)
{
if (ray_direction.y < 0)
{
contact_normal = { 0, 1 };
}
else
{
contact_normal = { 0, -1 };
}
}
return true;
}
bool DynamicRectVsRect(sf::RectangleShape& in, sf::RectangleShape target, sf::Vector2f in_rect_vel, sf::Vector2f& contact_point, sf::Vector2f& contact_normal, float& contact_time, float fElapsedTime)
{
if (in_rect_vel.x == 0 && in_rect_vel.y == 0)
return false;
sf::RectangleShape expanded_target;
expanded_target.setPosition(sf::Vector2f(target.getPosition().x - in.getSize().x / 2, target.getPosition().y - in.getSize().y / 2));
expanded_target.setSize(target.getSize() + in.getSize());
std::cout << "rect_vel: " << in_rect_vel.x * fElapsedTime << '\t' << in_rect_vel.y * fElapsedTime << std::endl;
std::cout << "deltaTime: " << fElapsedTime << std::endl;
if (RayVsRect(sf::Vector2f(in.getPosition().x + in.getSize().x / 2, in.getPosition().y + in.getSize().y / 2), in_rect_vel * fElapsedTime, expanded_target, contact_point, contact_normal, contact_time))
{
std::cout << "contact_time: " << contact_time << std::endl;
if (contact_time <= 1.0f)
{
return true;
}
}
return false;
}
sf::Vector2f normalize(sf::Vector2f vector) {
float lenght = sqrt(pow(vector.x, 2) + pow(vector.y, 2));
return sf::Vector2f(vector.x/lenght, vector.y/lenght);
}
#include <SFML/Graphics.hpp>
bool RayVsRect(sf::Vector2f ray_origin, sf::Vector2f ray_direction, sf::RectangleShape target, sf::Vector2f& contact_point, sf::Vector2f& contact_normal, float& t_hit_time);
bool DynamicRectVsRect(sf::RectangleShape& in, sf::RectangleShape target, sf::Vector2f in_rect_vel, sf::Vector2f& contact_point, sf::Vector2f& contact_normal, float& contact_time, float fElapsedTime);
sf::Vector2f normalize(sf::Vector2f vector);
int main()
{
sf::RenderWindow window(sf::VideoMode(800, 600), "My window");
window.setFramerateLimit(60);
std::vector<sf::RectangleShape> vRects;
vRects.push_back(sf::RectangleShape());
vRects[0].setPosition({10.0f, 10.0f});
vRects[0].setSize({30.0f, 20.0f});
sf::Vector2f velocity = {0, 0};
vRects.push_back(sf::RectangleShape());
vRects[1].setPosition({ 100.0f, 100.0f });
vRects[1].setSize({ 80.0f, 50.0f });
sf::RectangleShape sfml_rect;
sfml_rect.setPosition({100.0, 100.0});
sfml_rect.setSize({ 200.0, 200.0 });
sf::CircleShape circle;
int circle_rad = 5;
circle.setFillColor(sf::Color::Red);
circle.setRadius(circle_rad);
sf::Vector2f ray_point = {20.0f, 20.0f};
sf::Vector2f ray_direction;
sf::Vector2i MousePos;
sf::Vector2f cp, cn;
float ct;
float t;
sf::Vertex normal[2];
sf::Clock clock;
float deltaTime;
while (window.isOpen())
{
MousePos = sf::Mouse::getPosition(window);
ray_point = sf::Vector2f(vRects[0].getPosition().x+vRects[0].getSize().x/2, vRects[0].getPosition().y + vRects[0].getSize().y / 2);
ray_direction = sf::Vector2f(MousePos.x - ray_point.x, MousePos.y - ray_point.y);
deltaTime = clock.restart().asSeconds();
//std::cout << ray_direction.x << '\t' << ray_direction.y << std::endl;
if (sf::Mouse::isButtonPressed(sf::Mouse::Left)) {
sf::Vector2f norm_ray_direction = normalize(ray_direction);
velocity += sf::Vector2f(norm_ray_direction.x * 100.0f * deltaTime, norm_ray_direction.y * 100.0f * deltaTime);
}
//std::cout << velocity.x << '\t' << velocity.y << std::endl;
for (int i = 1; i < vRects.size(); i++) {
if (DynamicRectVsRect(vRects[0], vRects[i], velocity, cp, cn, ct, deltaTime)) {
velocity += sf::Vector2f(cn.x * std::abs(velocity.x) * (1-ct), cn.y * std::abs(velocity.y) * (1-ct));
}
}
vRects[0].move(velocity.x * deltaTime, velocity.y * deltaTime);
std::cout << velocity.x << '\t' << velocity.y << "\t\t" << vRects[0].getPosition().x << '\t' << vRects[0].getPosition().x << std::endl;
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
window.close();
}
}
window.clear(sf::Color::Black);
for (int i = 0; i < vRects.size(); i++) {
window.draw(vRects[i]);
}
window.display();
}
return 0;
}
bool RayVsRect(sf::Vector2f ray_origin, sf::Vector2f ray_direction, sf::RectangleShape target, sf::Vector2f& contact_point, sf::Vector2f& contact_normal, float& t_hit_near) {
sf::Vector2f t_near = sf::Vector2f((target.getPosition().x - ray_origin.x) / ray_direction.x,
(target.getPosition().y - ray_origin.y) / ray_direction.y);
sf::Vector2f t_far = sf::Vector2f((target.getPosition().x + target.getSize().x - ray_origin.x) / ray_direction.x,
(target.getPosition().y + target.getSize().y - ray_origin.y) / ray_direction.y);
std::cout << "ray_direction: " << ray_direction.x << '\t' << ray_direction.y << std::endl;
std::cout << "t_near: " << t_near.x << '\t' << t_near.y << std::endl;
std::cout << "t_far: " << t_far.x << '\t' << t_far.y << std::endl;
if (t_near.x > t_far.x) std::swap(t_near.x, t_far.x);
if (t_near.y > t_far.y) std::swap(t_near.y, t_far.y);
if (t_near.x > t_far.y || t_near.y > t_far.x) return false;
//std::cout << "t_near: " << t_near.x << '\t' << t_near.y << std::endl;
t_hit_near = std::max(t_near.x, t_near.y);
float t_hit_far = std::min(t_far.x, t_far.y);
if (t_hit_far < 0) return false;
contact_point = ray_origin + t_hit_near * ray_direction;
if (t_near.x > t_near.y)
{
if (ray_direction.x < 0)
{
contact_normal = { 1, 0 };
}
else
{
contact_normal = { -1, 0 };
}
}
else if (t_near.x < t_near.y)
{
if (ray_direction.y < 0)
{
contact_normal = { 0, 1 };
}
else
{
contact_normal = { 0, -1 };
}
}
return true;
}
bool DynamicRectVsRect(sf::RectangleShape& in, sf::RectangleShape target, sf::Vector2f in_rect_vel, sf::Vector2f& contact_point, sf::Vector2f& contact_normal, float& contact_time, float fElapsedTime)
{
if (in_rect_vel.x == 0 && in_rect_vel.y == 0)
return false;
sf::RectangleShape expanded_target;
expanded_target.setPosition(sf::Vector2f(target.getPosition().x - in.getSize().x / 2, target.getPosition().y - in.getSize().y / 2));
expanded_target.setSize(target.getSize() + in.getSize());
std::cout << "rect_vel: " << in_rect_vel.x * fElapsedTime << '\t' << in_rect_vel.y * fElapsedTime << std::endl;
std::cout << "deltaTime: " << fElapsedTime << std::endl;
if (RayVsRect(sf::Vector2f(in.getPosition().x + in.getSize().x / 2, in.getPosition().y + in.getSize().y / 2), in_rect_vel * fElapsedTime, expanded_target, contact_point, contact_normal, contact_time))
{
std::cout << "contact_time: " << contact_time << std::endl;
if (contact_time <= 1.0f)
{
return true;
}
}
return false;
}
sf::Vector2f normalize(sf::Vector2f vector) {
float lenght = sqrt(pow(vector.x, 2) + pow(vector.y, 2));
return sf::Vector2f(vector.x/lenght, vector.y/lenght);
}