Collision reworked into a Collision Namespace

Added basic player (Needs work)
This commit is contained in:
Bram Verhulst
2024-03-13 01:09:12 +01:00
parent d0781db9f0
commit d6c5cfa47b
12 changed files with 308 additions and 67 deletions

120
Engine/Collision.cpp Normal file
View File

@@ -0,0 +1,120 @@
#include "Collision.h"
#include "utils.h"
namespace Collision
{
bool PointVsRect(const Point2f p, const Collision::CollisionRect& r) {
return ( p.x >= r.pos.x && p.y >= r.pos.y && p.x < r.pos.x + r.size.x && p.y < r.pos.y + r.size.y );
}
bool RectVsRect(const Collision::CollisionRect& r1, const Collision::CollisionRect r2) {
return ( r1.pos.x < r2.pos.x + r2.size.x && r1.pos.x + r1.size.x > r2.pos.x && r1.pos.y < r2.pos.y + r2.size.y && r1.pos.y + r1.size.y > r2.pos.y );
}
bool RayVsRect(const Point2f& rayOrigin, const Point2f& rayDirection, const Collision::CollisionRect target, Point2f& contactPoint, Point2f& contactNormal,
float& t_HitNear) {
contactNormal = Point2f { 0, 0 };
contactPoint = Point2f { 0, 0 };
const Point2f inverseDirection = 1.0f / rayDirection;
// Calculate intersections with rectangle bounding axes
Point2f t_Near = Point2f{ target.pos.x - rayOrigin.x, target.pos.y - rayOrigin.y } * inverseDirection;
Point2f t_Far = Point2f{ target.pos.x + target.size.x - rayOrigin.x, target.pos.y + target.size.y - rayOrigin.y } * inverseDirection;
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;
// Sort distances
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);
// Early rejection
if (t_Near.x > t_Far.y || t_Near.y > t_Far.x)
return false;
// Closest 'time' will be the first contact
t_HitNear = std::max(t_Near.x, t_Near.y);
const float t_HitFar = std::min(t_Far.x, t_Far.y);
// Reject if ray direction is pointing away from object
if (t_HitFar < 0)
return false;
// Contact point of collision from parametric line equation
contactPoint = rayOrigin + t_HitNear * rayDirection;
if (t_Near.x > t_Near.y) {
if (inverseDirection.x < 0) {
contactNormal = Point2f { 1, 0 };
}
else {
contactNormal = Point2f { -1, 0 };
}
}
else if (t_Near.x < t_Near.y) {
if (inverseDirection.y < 0) {
contactNormal = Point2f { 0, 1 };
}
else {
contactNormal = Point2f { 0, -1 };
}
}
// If t_Near == t_Far, collision is diagonal so pointless
return true;
}
bool DynamicRectVsRect(const Collision::CollisionRect& dynamicRectangle, float ElapsedTime, const Collision::CollisionRect& staticRectangle,
Point2f& contactPoint, Point2f& contactNormal, float& contactTime) {
// Check if dynamic rectangle is actually moving - we assume rectangles are NOT in collision to start
if (dynamicRectangle.vel.x == 0 && dynamicRectangle.vel.y == 0) {
return false;
}
// Expand target rectangle by source dimensions
Collision::CollisionRect expandedTarget;
expandedTarget.pos = Point2f{staticRectangle.pos.x - (dynamicRectangle.size / 2).x, staticRectangle.pos.y - (dynamicRectangle.size / 2).y};
expandedTarget.size = staticRectangle.size + dynamicRectangle.size;
Point2f RayOrigin = dynamicRectangle.pos + dynamicRectangle.size / 2;
if (RayVsRect(RayOrigin, dynamicRectangle.vel * ElapsedTime, expandedTarget, contactPoint, contactNormal, contactTime)) {
return ( contactTime >= 0.0f && contactTime < 1.0f );
}
else {
return false;
}
return false;
}
bool ResolveDynamicRectVsRect(Collision::CollisionRect& dynamicRectangle, float ElapsedTime, Collision::CollisionRect* staticRectangle) {
Point2f contactPoint, contactNormal;
float contact_time = 0.0f;
if (DynamicRectVsRect(dynamicRectangle, ElapsedTime, *staticRectangle, contactPoint, contactNormal, contact_time)) {
if (contactNormal.y > 0) {
dynamicRectangle.contact[0] = staticRectangle;
}
if (contactNormal.x < 0) {
dynamicRectangle.contact[1] = staticRectangle;
}
if (contactNormal.y < 0) {
dynamicRectangle.contact[2] = staticRectangle;
}
if (contactNormal.x > 0) {
dynamicRectangle.contact[3] = staticRectangle;
}
//dynamicRectangle.vel = dynamicRectangle.vel + contactNormal * Point2f(std::abs(dynamicRectangle.vel.x), std::abs(dynamicRectangle.vel.y)) * ( 1 - contact_time );
dynamicRectangle.vel = dynamicRectangle.vel + contactNormal * -utils::DotProduct(dynamicRectangle.vel, contactNormal) * ( 1 - contact_time );
return true;
}
return false;
}
}

44
Engine/Collision.h Normal file
View File

@@ -0,0 +1,44 @@
#pragma once
#include <array>
#include <cmath>
#include "structs.h"
#include "utils.h"
class WorldTile;
namespace Collision
{
struct CollisionRect
{
Point2f pos;
Point2f size;
Point2f vel;
std::array<Collision::CollisionRect*, 4> contact;
};
struct TileCollisionRect : public CollisionRect
{
TileCollisionRect(const Point2f& pos, const Point2f& size, WorldTile* tile)
: CollisionRect{ pos, size, Point2f{ 0, 0 }, { nullptr, nullptr, nullptr, nullptr } }
, tile{ tile }
{}
WorldTile* tile;
bool Contains(Point2f point2_f) {
return utils::IsPointInRect(point2_f, Rectf{ pos.x, pos.y, size.x, size.y });
}
};
bool PointVsRect(const Point2f p, const CollisionRect& r);
bool RectVsRect(const CollisionRect& r1, const CollisionRect r2);
bool RayVsRect(const Point2f& rayOrigin, const Point2f& rayDirection, const Collision::CollisionRect target, Point2f& contactPoint, Point2f& contactNormal, float& t_HitNear);
bool DynamicRectVsRect(const CollisionRect& dynamicRectangle, float ElapsedTime, const CollisionRect& staticRectangle, Point2f& contactPoint, Point2f& contactNormal, float& contactTime);
bool ResolveDynamicRectVsRect(CollisionRect& dynamicRectangle, float ElapsedTime, CollisionRect* staticRectangle);
}

View File

@@ -20,6 +20,7 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="BaseGame.cpp" />
<ClCompile Include="Collision.cpp" />
<ClCompile Include="Matrix2x3.cpp" />
<ClCompile Include="SoundEffect.cpp" />
<ClCompile Include="SoundStream.cpp" />
@@ -31,6 +32,7 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="BaseGame.h" />
<ClInclude Include="Collision.h" />
<ClInclude Include="colors.h" />
<ClInclude Include="Matrix2x3.h" />
<ClInclude Include="base.h" />

View File

@@ -23,6 +23,9 @@ Point2f::Point2f(float x, float y)
Point2f Point2f::operator+(const Point2f& other) const {
return Point2f { x + other.x, y + other.y };
}
Point2f Point2f::operator+=(const Point2f& other) const {
return Point2f { x + other.x, y + other.y };
}
Point2f Point2f::operator*(float other) const {
return Point2f { x * other, y * other };
}
@@ -35,11 +38,20 @@ Point2f Point2f::operator*(int other) const {
Point2f Point2f::operator/(float other) const {
return Point2f { x / other, y / other };
}
Point2f Point2f::operator-(const Point2f& other) const {
return Point2f { x - other.x, y - other.y };
}
// Point2f::Point2f(int x, int y)
// : x { (float)x }, y { (float)y } {
// }
Point2f operator/(float right, const Point2f& left) {
return Point2f { right / left.x, right / left.y };
}
Point2f operator*(float right, const Point2f& left) {
return Point2f{ right * left.x, right * left.y };
}
//-----------------------------------------------------------------
// Rectf Constructors
//-----------------------------------------------------------------

View File

@@ -20,16 +20,19 @@ struct Point2f
//operator
Point2f operator+( const Point2f& other ) const;
Point2f operator+=( const Point2f& other ) const;
Point2f operator*( float other ) const;
Point2f operator*( const Point2f& other ) const;
Point2f operator*( int other ) const;
Point2f operator*( const Point2f& other ) const;
Point2f operator/( float other ) const;
Point2f operator-( const Point2f& other ) const;
float x;
float y;
};
Point2f operator/(float right, const Point2f& left);
Point2f operator*(float right, const Point2f& left);
struct Rectf
{

View File

@@ -580,7 +580,7 @@ bool utils::Raycast( const Point2f* vertices, const size_t nrVertices, const Poi
HitInfo linesHitInfo{};
linesHitInfo.lambda = lambda1;
linesHitInfo.intersectPoint = Point2f{ rayP1.x + ( ( rayP2.x - rayP1.x ) * lambda1 ), rayP1.y + ( ( rayP2.y - rayP1.y ) * lambda1 ) };
linesHitInfo.normal = Vector2f{ q2 - q1 }.Orthogonal( ).Normalized( );
linesHitInfo.normal = Vector2f{ q2.x - q1.x, q2.y - q2.y }.Orthogonal( ).Normalized( );
hits.push_back(linesHitInfo);
}
}
@@ -645,7 +645,7 @@ float utils::DistPointLineSegment( const Point2f& p, const Point2f& a, const Po
// Closest point is between A and B, calc intersection point
Vector2f intersection{ abNorm.DotProduct(ap) * abNorm + Vector2f{ a } };
return Vector2f{ p - intersection }.Length( );
return Vector2f{ p.x - intersection.x, p.y - intersection.y }.Length( );
}
bool utils::IntersectRectLine(const Rectf& r, const Point2f& p1, const Point2f& p2, float& intersectMin, float& intersectMax)
@@ -693,9 +693,12 @@ bool utils::IsRectInRect(const Rectf& r1, const Rectf& r2) {
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};
// 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};
Point2f t_near{};
Point2f t_far{};
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;
@@ -724,7 +727,6 @@ bool utils::RayVsRect(const Point2f& rayOrigin, const Point2f& rayDir, const Rec
contactNormal = Point2f{0, -1};
}
}
return true;
}

View File

@@ -1,6 +1,8 @@
#pragma once
#include <vector>
#include "Vector2f.h"
#include "structs.h"
#include "SDL.h"
namespace utils
{