mirror of
https://github.com/HowestDAE/dae16-VerhulstBram.git
synced 2025-12-16 03:41:48 +01:00
Added a (temp) collision solver for Axis-Aligned rectangles
This commit is contained in:
@@ -36,6 +36,8 @@ struct Rectf
|
||||
Rectf( );
|
||||
explicit Rectf( float left, float bottom, float width, float height );
|
||||
//explicit Rectf( int left, int bottom, int width, int height ); //Stupid fix for it giving an error (same as Point2f)
|
||||
|
||||
Point2f BottomLeft() const { return Point2f{ left, bottom }; }
|
||||
|
||||
float left;
|
||||
float bottom;
|
||||
|
||||
@@ -686,9 +686,87 @@ bool utils::IntersectRectLine(const Rectf& r, const Point2f& p1, const Point2f&
|
||||
intersectMax = tMax;
|
||||
return true;
|
||||
}
|
||||
bool utils::IsRectInRect(const Rectf& r1, const Rectf& r2) {
|
||||
// the origin of both rectangles is in bottom left
|
||||
return (r1.left < r2.left + r2.width && r1.left + r1.width > r2.left && r1.bottom < r2.bottom + r2.height && r1.bottom + r1.height > r2.bottom);
|
||||
}
|
||||
bool utils::RayVsRect(const Point2f& rayOrigin, const Point2f& rayDir, const Rectf& target,
|
||||
Point2f& contactPoint, Point2f& contactNormal, float& t_hit_near) {
|
||||
|
||||
Point2f t_near = Point2f{(target.BottomLeft() - rayOrigin).x / rayDir.x, (target.BottomLeft() - rayOrigin).y / rayDir.y};
|
||||
Point2f t_far = Point2f{(target.BottomLeft() + Point2f{target.width, target.height} - rayOrigin).x / rayDir.x, (target.BottomLeft() + Point2f{target.width, target.height} - rayOrigin).y / rayDir.y};
|
||||
|
||||
if(std::isnan(t_far.y) || std::isnan(t_far.x)) return false;
|
||||
if(std::isnan(t_near.y) || std::isnan(t_near.x)) return false;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
contactPoint = rayOrigin + rayDir * t_hit_near;
|
||||
|
||||
if(t_near.x > t_near.y) {
|
||||
if(rayDir.x < 0) {
|
||||
contactNormal = Point2f{1, 0};
|
||||
}else {
|
||||
contactNormal = Point2f{-1, 0};
|
||||
}
|
||||
} else if(t_near.x < t_near.y) {
|
||||
if(rayDir.y < 0) {
|
||||
contactNormal = Point2f{0, 1};
|
||||
}else {
|
||||
contactNormal = Point2f{0, -1};
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool utils::DynamicRectVsRect(const MovingRectf& in, const Rectf& target, Point2f& contactPoint, Point2f& contactNormal, float& contactTime, float dt) {
|
||||
if(in.velocity.x == 0 && in.velocity.y == 0) return false;
|
||||
|
||||
|
||||
Rectf expanded_target{};
|
||||
expanded_target.left = target.left - in.width / 2;
|
||||
expanded_target.bottom = target.bottom - in.height / 2;
|
||||
expanded_target.width = target.width + in.width;
|
||||
expanded_target.height = target.height + in.height;
|
||||
|
||||
if(RayVsRect(Point2f{in.bottomLeft.x + in.width / 2, in.bottomLeft.y + in.height / 2}, in.velocity * dt, expanded_target, contactPoint, contactNormal, contactTime)) {
|
||||
if(contactTime <= 1.0f && contactTime >= 0.0f) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
float utils::DotProduct(const Point2f& a, const Point2f& b) {
|
||||
return a.x * b.x + a.y * b.y;
|
||||
}
|
||||
#pragma endregion CollisionFunctionality
|
||||
|
||||
int utils::randRange(int min, int max) {
|
||||
return min + rand() % (( max + 1 ) - min);
|
||||
}
|
||||
bool utils::isKeyDown(int keycode) {
|
||||
const Uint8* pStates = SDL_GetKeyboardState(nullptr);
|
||||
if (pStates != nullptr)
|
||||
{
|
||||
if(pStates[keycode]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool utils::isMouseDown(int button) {
|
||||
const Uint32 pStates = SDL_GetMouseState(nullptr, nullptr);
|
||||
if (pStates & SDL_BUTTON(button)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,15 @@ namespace utils
|
||||
{
|
||||
const float g_Pi{ 3.1415926535f };
|
||||
|
||||
struct MovingRectf
|
||||
{
|
||||
Point2f bottomLeft;
|
||||
float width;
|
||||
float height;
|
||||
|
||||
Point2f velocity;
|
||||
};
|
||||
|
||||
#pragma region OpenGLDrawFunctionality
|
||||
|
||||
void SetColor( const Color4f& color );
|
||||
@@ -75,6 +84,7 @@ namespace utils
|
||||
bool IsOverlapping( const Circlef& c1, const Circlef& c2 );
|
||||
bool IsOverlapping( const std::vector<Point2f>& vertices, const Circlef& c );
|
||||
bool IsOverlapping( const Point2f* vertices, size_t nrVertices, const Circlef& c );
|
||||
|
||||
bool Raycast( const Point2f* vertices, const size_t nrVertices, const Point2f& rayP1, const Point2f& rayP2, HitInfo& hitInfo );
|
||||
bool Raycast( const std::vector<Point2f>& vertices, const Point2f& rayP1, const Point2f& rayP2, HitInfo& hitInfo );
|
||||
|
||||
@@ -82,8 +92,19 @@ namespace utils
|
||||
float DistPointLineSegment(const Point2f& p, const Point2f& a, const Point2f& b);
|
||||
bool IsPointOnLineSegment(const Point2f& p, const Point2f& a, const Point2f& b);
|
||||
bool IntersectRectLine(const Rectf& r, const Point2f& p1, const Point2f& p2, float& intersectMin, float& intersectMax);
|
||||
bool IsRectInRect(const Rectf& r1, const Rectf& r2);
|
||||
|
||||
bool RayVsRect(const Point2f& rayOrigin, const Point2f& rayDir, const Rectf& target,
|
||||
Point2f& contactPoint, Point2f& contactNormal, float& contactTime);
|
||||
|
||||
bool DynamicRectVsRect(const MovingRectf& in, const Rectf& target, Point2f& contactPoint, Point2f& contactNormal, float& contactTime, float dt);
|
||||
|
||||
float DotProduct(const Point2f& a, const Point2f& b);
|
||||
|
||||
int randRange(int min, int max);
|
||||
#pragma endregion CollisionFunctionality
|
||||
|
||||
bool isKeyDown(SDL_Keycode keycode);
|
||||
bool isMouseDown(int button);
|
||||
|
||||
}
|
||||
@@ -5,6 +5,8 @@ Camera::Camera() : m_Position { 0, 0 }, m_Scale { 1.0f } {
|
||||
}
|
||||
Camera::Camera(const Point2f& position, const float scale) : m_Position { position }, m_Scale { scale } {
|
||||
}
|
||||
Camera::~Camera() {
|
||||
}
|
||||
|
||||
void Camera::BeginRendering() const {
|
||||
glPushMatrix();
|
||||
@@ -16,6 +18,6 @@ void Camera::EndRendering() const {
|
||||
glPopMatrix();
|
||||
}
|
||||
Point2f Camera::TransformMouse(const Point2f& mousePos) const {
|
||||
const Point2f translatedPosition = mousePos + m_Position;
|
||||
const Point2f translatedPosition = Point2f{ mousePos.x - m_Position.x, Viewport.height - mousePos.y - m_Position.y};
|
||||
return translatedPosition;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ class Camera
|
||||
public:
|
||||
Camera( );
|
||||
Camera( const Point2f& position, float scale = 1);
|
||||
virtual ~Camera();
|
||||
|
||||
void SetPosition( const Point2f& position ) { m_Position = position; }
|
||||
void SetScale( const float scale ) { m_Scale = scale; }
|
||||
@@ -16,6 +17,8 @@ public:
|
||||
void EndRendering() const;
|
||||
|
||||
Point2f TransformMouse (const Point2f& mousePos) const;
|
||||
Rectf Viewport = Rectf{ 0, 0, 846.f, 500.f };
|
||||
//TODO: Remove this and make it some static
|
||||
|
||||
private:
|
||||
Point2f m_Position;
|
||||
|
||||
@@ -44,6 +44,8 @@ void Game::Update(float elapsedSec) {
|
||||
m_MouseOffset = m_Camera.GetPosition();
|
||||
}
|
||||
|
||||
m_WorldLevel.Update(elapsedSec);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,4 @@ Level::Level(Camera* camera) {
|
||||
m_pCamera = camera;
|
||||
}
|
||||
Level::~Level() {
|
||||
}
|
||||
void Level::Update(float elapsedSec) {
|
||||
}
|
||||
void Level::Draw() const {
|
||||
}
|
||||
void Level::MouseMove(const Point2f& mousePos) {
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,9 @@ public:
|
||||
Level(Camera* camera);
|
||||
virtual ~Level();
|
||||
|
||||
virtual void Update(float elapsedSec);
|
||||
virtual void Draw() const;
|
||||
virtual void MouseMove(const Point2f& mousePos);
|
||||
virtual void Update(float elapsedSec) = 0;
|
||||
virtual void Draw() const = 0;
|
||||
virtual void MouseMove(const Point2f& mousePos) = 0;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -14,11 +14,30 @@ void Player::Draw() const {
|
||||
}
|
||||
|
||||
void Player::Update(float elapsedTime, const WorldLevel& level) {
|
||||
Point2f acc{0, 0};
|
||||
acc.y += m_Gravity.y;
|
||||
// m_Acc.y += m_Gravity.y;
|
||||
// m_Vel.y = std::min(m_Vel.y, m_MaxSpeed);
|
||||
//
|
||||
// Point2f nextPos = m_Position + m_Vel * elapsedTime;
|
||||
// //collision checking
|
||||
m_Vel = Point2f{0, -100};
|
||||
|
||||
Point2f nextPos = m_Position + m_Vel * elapsedTime * acc * elapsedTime * elapsedTime;
|
||||
//collision checking
|
||||
//check for keys
|
||||
if(utils::isKeyDown(SDL_SCANCODE_W)) {
|
||||
m_Vel.y = 100;
|
||||
}
|
||||
if(utils::isKeyDown(SDL_SCANCODE_S)) {
|
||||
m_Vel.y = -100;
|
||||
}
|
||||
if(utils::isKeyDown(SDL_SCANCODE_A)) {
|
||||
m_Vel.x = -100;
|
||||
}
|
||||
if(utils::isKeyDown(SDL_SCANCODE_D)) {
|
||||
m_Vel.x = 100;
|
||||
}
|
||||
|
||||
Point2f nextPos = m_Position + m_Vel * elapsedTime;
|
||||
bool isColliding = false;
|
||||
|
||||
auto tiles = level.GetAllTiles();
|
||||
for (int x{0}; x < WorldLevel::WORLD_WIDTH; ++x) {
|
||||
for (int y{0}; y < WorldLevel::WORLD_HEIGHT; ++y) {
|
||||
@@ -26,20 +45,13 @@ void Player::Update(float elapsedTime, const WorldLevel& level) {
|
||||
if (tile->GetTileType() == GroundTileTypes::Dirt) {
|
||||
Rectf tileRect = Rectf{tile->GetPosition().x, tile->GetPosition().y, WorldLevel::TILE_WIDTH, WorldLevel::TILE_HEIGHT};
|
||||
if (utils::IsOverlapping(nextPos, m_Size, tileRect)) {
|
||||
//collision
|
||||
if (m_Vel.y < 0) {
|
||||
//collision from above
|
||||
m_Position.y = tileRect.bottom;
|
||||
m_Vel.y = 0;
|
||||
} else {
|
||||
m_Position.y = tileRect.bottom + m_Size.y;
|
||||
m_Vel.y = 0;
|
||||
}
|
||||
isColliding = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_Vel = Point2f{m_Vel.x + float(acc.x * elapsedTime), m_Vel.y + float(acc.y * elapsedTime)};
|
||||
// m_Position += m_Vel * elapsedTime;
|
||||
m_Position = Point2f{m_Position.x + m_Vel.x * elapsedTime, m_Position.y + m_Vel.y * elapsedTime};
|
||||
|
||||
if(!isColliding) {
|
||||
m_Position = nextPos;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "pch.h"
|
||||
#include "WorldLevel.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
|
||||
@@ -16,6 +17,21 @@ WorldLevel::WorldLevel(Camera* camera) : Level(camera), m_mousePos{ 0, 0 }, m_pl
|
||||
m_worldTiles[x][y] = new WorldTile{ Point2f{ float(actualX * TILE_WIDTH), -float(y * TILE_HEIGHT) - TILE_HEIGHT}, GroundTileTypes::Dirt};
|
||||
}
|
||||
}
|
||||
|
||||
m_Rects.push_back(utils::MovingRectf{Point2f{20, 40}, 40, 50, Point2f{0,0}});
|
||||
m_Rects.push_back(utils::MovingRectf{Point2f{510.0f, 210.0f}, 30.0f, 120.0f, Point2f{0, 0}});
|
||||
m_Rects.push_back(utils::MovingRectf{Point2f{450.0f, 150.0f}, 60.0f, 60.0f, Point2f{0, 0}});
|
||||
m_Rects.push_back(utils::MovingRectf{Point2f{450.0f, 450.0f}, 225.0f, 60.0f, Point2f{0, 0}});
|
||||
m_Rects.push_back(utils::MovingRectf{Point2f{510.0f, 150.0f}, 60.0f, 60.0f, Point2f{0, 0}});
|
||||
m_Rects.push_back(utils::MovingRectf{Point2f{570.0f, 150.0f}, 60.0f, 60.0f, Point2f{0, 0}});
|
||||
m_Rects.push_back(utils::MovingRectf{Point2f{330.0f, 150.0f}, 60.0f, 60.0f, Point2f{0, 0}});
|
||||
m_Rects.push_back(utils::MovingRectf{Point2f{150.0f, 390.0f}, 60.0f, 60.0f, Point2f{0, 0}});
|
||||
m_Rects.push_back(utils::MovingRectf{Point2f{150.0f, 450.0f}, 60.0f, 60.0f, Point2f{0, 0}});
|
||||
m_Rects.push_back(utils::MovingRectf{Point2f{150.0f, 510.0f}, 60.0f, 60.0f, Point2f{0, 0}});
|
||||
m_Rects.push_back(utils::MovingRectf{Point2f{450.0f, 300.0f}, 30.0f, 3.0f, Point2f{0, 0}});
|
||||
m_Rects.push_back(utils::MovingRectf{Point2f{600.0f, 300.0f}, 60.0f, 180.0f, Point2f{0, 0}});
|
||||
|
||||
|
||||
|
||||
// std::string dirtPath = + "tiles/dirt/dirt" + std::to_string(utils::randRange(1, 5)) + ".png";
|
||||
// m_pTextTexture = new Texture(dirtPath);
|
||||
@@ -28,30 +44,111 @@ void WorldLevel::Update(float elapsedSec) {
|
||||
int mouseX, mouseY;
|
||||
SDL_GetMouseState(&mouseX, &mouseY);
|
||||
m_mousePos = Point2f{ float(mouseX), float(mouseY) };
|
||||
m_player.Update(elapsedSec, *this);
|
||||
m_mousePos = m_pCamera->TransformMouse(m_mousePos);
|
||||
//m_player.Update(elapsedSec, *this);
|
||||
|
||||
|
||||
|
||||
|
||||
Point2f RayPoint = Point2f{m_pCamera->Viewport.width, m_pCamera->Viewport.height};
|
||||
Point2f RayDir = Point2f{m_mousePos.x - RayPoint.x, m_mousePos.y - RayPoint.y};
|
||||
|
||||
//wasd movement
|
||||
if(utils::isKeyDown(SDL_SCANCODE_W)) {
|
||||
m_Rects[0].velocity.y += 10;
|
||||
}
|
||||
if(utils::isKeyDown(SDL_SCANCODE_S)) {
|
||||
m_Rects[0].velocity.y += -10;
|
||||
}
|
||||
if(utils::isKeyDown(SDL_SCANCODE_A)) {
|
||||
m_Rects[0].velocity.x += -10;
|
||||
}
|
||||
if(utils::isKeyDown(SDL_SCANCODE_D)) {
|
||||
m_Rects[0].velocity.x += 10;
|
||||
}
|
||||
|
||||
Point2f intersectionPoint, normal;
|
||||
float contactTime;
|
||||
|
||||
std::vector<std::pair<int, float>> contactTimes{};
|
||||
|
||||
for (size_t i { 1 }; i < m_Rects.size(); ++i) {
|
||||
Rectf target = Rectf{m_Rects[i].bottomLeft.x, m_Rects[i].bottomLeft.y, m_Rects[i].width, m_Rects[i].height};
|
||||
if(utils::DynamicRectVsRect(m_Rects[0], target, intersectionPoint, normal, contactTime, elapsedSec)) {
|
||||
// m_Rects[0].velocity.x += normal.x * (1 - contactTime) * -utils::DotProduct( m_Rects[0].velocity, normal);
|
||||
// m_Rects[0].velocity.y += normal.y * (1 - contactTime) * -utils::DotProduct( m_Rects[0].velocity, normal);
|
||||
//
|
||||
// //m_Rects[0].velocity = Point2f{0, 0};
|
||||
|
||||
contactTimes.push_back({i, contactTime});
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(contactTimes.begin(), contactTimes.end(), [](const std::pair<int, float>& a, const std::pair<int, float>& b) {
|
||||
return a.second < b.second;
|
||||
});
|
||||
|
||||
for (std::pair<int, float> contact : contactTimes) {
|
||||
Rectf target = Rectf{m_Rects[contact.first].bottomLeft.x, m_Rects[contact.first].bottomLeft.y, m_Rects[contact.first].width, m_Rects[contact.first].height};
|
||||
if(utils::DynamicRectVsRect(m_Rects[0], target, intersectionPoint, normal, contactTime, elapsedSec)) {
|
||||
m_Rects[0].velocity.x += normal.x * (1 - contactTime) * -utils::DotProduct( m_Rects[0].velocity, normal);
|
||||
m_Rects[0].velocity.y += normal.y * (1 - contactTime) * -utils::DotProduct( m_Rects[0].velocity, normal);
|
||||
}
|
||||
}
|
||||
|
||||
m_Rects[0].bottomLeft.x = m_Rects[0].bottomLeft.x + m_Rects[0].velocity.x * elapsedSec;
|
||||
m_Rects[0].bottomLeft.y = m_Rects[0].bottomLeft.y + m_Rects[0].velocity.y * elapsedSec;
|
||||
|
||||
}
|
||||
void WorldLevel::Draw() const {
|
||||
m_pCamera->BeginRendering();
|
||||
|
||||
for (utils::MovingRectf rect : m_Rects) {
|
||||
utils::DrawRect(rect.bottomLeft, rect.width, rect.height);
|
||||
}
|
||||
|
||||
Rectf expanded = Rectf{ m_Rects[1].bottomLeft.x - m_Rects[0].width / 2, m_Rects[1].bottomLeft.y - m_Rects[0].height / 2, m_Rects[1].width + m_Rects[0].width, m_Rects[1].height + m_Rects[0].height };
|
||||
utils::SetColor(Colors::RED);
|
||||
utils::DrawRect(expanded);
|
||||
|
||||
|
||||
Point2f RayPoint = Point2f{m_pCamera->Viewport.width, m_pCamera->Viewport.height};
|
||||
Point2f RayDir = Point2f{m_mousePos.x - RayPoint.x, m_mousePos.y - RayPoint.y};
|
||||
|
||||
utils::SetColor(Colors::WHITE);
|
||||
utils::DrawLine(RayPoint, m_mousePos);
|
||||
|
||||
utils::FillEllipse(m_mousePos, 20, 20);
|
||||
|
||||
Point2f intersectionPoint, normal;
|
||||
float contactTime;
|
||||
Rectf testRect = Rectf{m_Rects[1].bottomLeft.x, m_Rects[1].bottomLeft.y, m_Rects[1].width, m_Rects[1].height};
|
||||
if(utils::RayVsRect(RayPoint, RayDir, testRect, intersectionPoint, normal, contactTime) && contactTime > 0.0f && contactTime < 1.0f) {
|
||||
utils::SetColor(Colors::GREEN);
|
||||
utils::FillEllipse(intersectionPoint, 5, 5);
|
||||
utils::DrawLine(intersectionPoint, Point2f{intersectionPoint.x + normal.x * 50, intersectionPoint.y + normal.y * 50});
|
||||
}
|
||||
|
||||
|
||||
// utils::SetColor(Colors::YELLOW);
|
||||
// utils::FillEllipse(m_mousePos,2,2);
|
||||
for (size_t x { 0 }; x < WORLD_WIDTH; ++x) {
|
||||
for (size_t y { 0 }; y < WORLD_HEIGHT; ++y) {
|
||||
m_worldTiles[x][y]->Draw();
|
||||
}
|
||||
}
|
||||
|
||||
utils::SetColor(Colors::WHITE);
|
||||
for (int x { -100 }; x < 100; ++x) {
|
||||
for (int y { -100 }; y < 100; ++y) {
|
||||
utils::DrawLine(x * 50, -5000, x * 50, 50000);
|
||||
utils::DrawLine(-5000, y * 50, 50000, y * 50);
|
||||
}
|
||||
}
|
||||
|
||||
// utils::SetColor(Colors::WHITE);
|
||||
// for (int x { -100 }; x < 100; ++x) {
|
||||
// for (int y { -100 }; y < 100; ++y) {
|
||||
// utils::DrawLine(x * 50, -5000, x * 50, 50000);
|
||||
// utils::DrawLine(-5000, y * 50, 50000, y * 50);
|
||||
// }
|
||||
// }
|
||||
utils::SetColor(Colors::MAGENTA);
|
||||
utils::FillEllipse(0, 0, 10, 10);
|
||||
utils::FillEllipse(0, 0, 5, 5);
|
||||
|
||||
m_player.Draw();
|
||||
//m_player.Draw();
|
||||
m_pCamera->EndRendering();
|
||||
|
||||
//utils::SetColor(Colors::WHITE);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <array>
|
||||
|
||||
#include "Player.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
class WorldLevel : public Level {
|
||||
@@ -29,6 +30,8 @@ public:
|
||||
|
||||
std::array<std::array<WorldTile*, WORLD_WIDTH>, WORLD_HEIGHT> GetAllTiles() const;
|
||||
|
||||
std::vector<utils::MovingRectf> m_Rects;
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
Reference in New Issue
Block a user