added collision detection with border [WIP] - need to improve accuracy not to go through walls at connection points

This commit is contained in:
Sylvain PILLOT 2023-05-22 00:26:08 +02:00
parent 7662a06e2c
commit 88351e0bd5
6 changed files with 74 additions and 212 deletions

View File

@ -134,14 +134,11 @@ void Level::Render( void )
((int) MyLevelBorders[i]->A.x+(int) MyLevelBorders[i]->B.x)/2 + (int) MyLevelBorders[i]->N.x/2, ((int) MyLevelBorders[i]->A.y+(int) MyLevelBorders[i]->B.y)/2 + (int) MyLevelBorders[i]->N.y/2,
C_BLUE );
#endif
}
void Level::RenderSelected( uint8_t i, uint8_t j )
{
//azrp_image_p8( i*16, j*16, &img_selected, DIMAGE_NONE );
//azrp_draw_text( i*16+1, j*16+1, "B=%d", GetTileBackgroundINT( i, j ) );
//azrp_draw_text( i*16+1, j*16+1, "F=%d", GetTileForegroundINT( i, j ) );
}
@ -191,64 +188,60 @@ int Level::GetTileForeground( uint16_t x, uint16_t y )
}
bool CollisionDroiteSeg(Point2D A,Point2D B,Point2D O,Point2D P)
bool CollisionSegSeg( Point2D A, Point2D B, Point2D C, Point2D D, Point2D *R )
{
Vector2D AO,AP,AB;
AB.x = B.x - A.x;
AB.y = B.y - A.y;
AP.x = P.x - A.x;
AP.y = P.y - A.y;
AO.x = O.x - A.x;
AO.y = O.y - A.y;
libnum::num part1 = AB.x*AP.y - AB.y*AP.x;
libnum::num part2 = AB.x*AO.y - AB.y*AO.x;
if (part1*part2<0)
return true;
else
return false;
}
Vector2D I, J;
bool CollisionSegSeg( Point2D A, Point2D B, Point2D O, Point2D P)
{
if (CollisionDroiteSeg(A,B,O,P)==false)
return false; // inutile d'aller plus loin si le segment [OP] ne touche pas la droite (AB)
Vector2D AB,OP;
AB.x = B.x - A.x;
AB.y = B.y - A.y;
OP.x = P.x - O.x;
OP.y = P.y - O.y;
I.x = B.x - A.x;
I.y = B.y - A.y;
if (I.x==0 && I.y==0) return false;
libnum::num part1 = (A.x*OP.y-O.x*OP.y-OP.x*A.y+OP.x*O.y);
libnum::num part2 = (AB.x*OP.y-AB.y*OP.x);
libnum::num k = - part1 / part2;
if (k<0 || k>1)
return false;
else
return true;
J.x = D.x - C.x;
J.y = D.y - C.y;
if (J.x==0 && J.y==0) return false;
libnum::num32 det;
det = I.x*J.y-I.y*J.x;
if (det==0) return false; // the segments are parallel
libnum::num32 m;
m = (I.x*A.y - I.x*C.y - I.y*A.x + I.y*C.x) / det;
libnum::num32 k;
k = (J.x*A.y - J.x*C.y - J.y*A.x + J.y*C.x) / det;
if ((m<libnum::num32(0) || m>libnum::num32(1) || k<libnum::num32(0) || k>libnum::num32(1)))
return false; // intersection of the lines, but not of the segments
(*R).x = C.x + k*J.x;
(*R).y = C.y + k*J.y;
return true;
}
/*RETURN true if the player can go in the target position*/
bool Level::CanGo( Player *MyPlayer )
bool Level::CanGo( void )
{
Point2D PlayerOrigin, PlayerTarget;
PlayerOrigin.x = MyPlayer->currx;
PlayerOrigin.y = MyPlayer->curry;
PlayerOrigin.x = MyPlayer.currx;
PlayerOrigin.y = MyPlayer.curry;
PlayerTarget.x = MyPlayer->nextx;
PlayerTarget.y = MyPlayer->nexty;
PlayerTarget.x = MyPlayer.nextx;
PlayerTarget.y = MyPlayer.nexty;
if (PlayerTarget.x == PlayerOrigin.x && PlayerTarget.y == PlayerOrigin.y) return true;
if (MyLevelBorders.size() == 0) return true;
if (PlayerTarget.x == PlayerOrigin.x && PlayerTarget.y == PlayerOrigin.y) return true; // velocity is 0 so Player.next = Player.Curr so we can stay here, that's obvious
if (MyLevelBorders.size() == 0) return true; // there is no surrounding border so no need to check if there are collisions (also obvious)
for (unsigned int i=0; i<=MyLevelBorders.size(); i++)
for (unsigned int i=0; i<MyLevelBorders.size(); i++)
{
Point2D A, B;
Point2D A, B, I;
A.x = MyLevelBorders[i]->A.x;
A.y = MyLevelBorders[i]->A.y;
@ -256,33 +249,26 @@ bool Level::CanGo( Player *MyPlayer )
B.x = MyLevelBorders[i]->B.x;
B.y = MyLevelBorders[i]->B.y;
if (CollisionSegSeg( A, B, PlayerOrigin, PlayerTarget ))
// Detection de collision entre [A,B] et [Player.Curr, Player.Next]
if (CollisionSegSeg( A, B, PlayerOrigin, PlayerTarget, &I ))
{
MyLevelBorders[i]->color = C_RED;
return false;
MyPlayer.nextx = I.x;
MyPlayer.nexty = I.y;
MyPlayer.vx = libnum::num32(0);
MyPlayer.vy = libnum::num32(0);
return false;
}
else
{
MyLevelBorders[i]->color = C_RGB( 255, 0, 255 );
}
}
return true;
}
/*RETURN true if the player is above a solid tile*/
/*TO DO : TO BE IMPROVED, THIS IS REALLY DIRTY !!!!*/
bool Level::IsOnGround( Player *MyPlayer )
{
if (this->GetTileBackgroundINT( (int) MyPlayer->currx, (int) MyPlayer->curry +1 ) !=0)
{
return true;
}
return false;
}
Border* Level::MakeBorder( libnum::num DX, libnum::num DY, float x1, float y1, float x2, float y2 )
{
@ -302,7 +288,6 @@ Border* Level::MakeBorder( libnum::num DX, libnum::num DY, float x1, float y1, f
return Bord;
}
void Level::ConvertTileToBorder( int16_t currentTile, libnum::num DeltaX, libnum::num DeltaY )
{
Border *B1;
@ -550,33 +535,6 @@ void Level::ConvertTileToBorder( int16_t currentTile, libnum::num DeltaX, libnum
}
}
#if 0
void Level::UpdateBorders( void )
{
for(unsigned int i=0; i<MyLevelBorders.size();i++)
delete MyLevelBorders[i];
MyLevelBorders.clear();
for( int i=0; i<map_level->w; i++)
{
for( int j=0; j<map_level->h; j++)
{
uint16_t index = j * map_level->w + i;
int16_t currentTile = map_level->layers[0][ index ];
libnum::num DeltaX = libnum::num( i*16 );
libnum::num DeltaY = libnum::num( j*16 );
ConvertTileToBorder( currentTile, DeltaX, DeltaY );
}
}
}
#else
void Level::UpdateBorders( void )
{
for(unsigned int i=0; i<MyLevelBorders.size(); i++)
@ -609,5 +567,3 @@ void Level::UpdateBorders( void )
}
}
}
#endif

View File

@ -41,8 +41,7 @@ class Level
void ConvertTileToBorder( int16_t currentTile, libnum::num DeltaX, libnum::num DeltaY );
void UpdateBorders( void );
bool CanGo( Player *MyPlayer );
bool IsOnGround( Player *MyPlayer );
bool CanGo( void );
private:
int GetTileBackground( uint16_t x, uint16_t y );

View File

@ -122,7 +122,7 @@ static void render( void )
#if(BIAS)
if (texttodraw>=1) azrp_draw_text(1,01, "FPS = %.0f - Mem Free = %d", (float) (1000.0f / elapsedTime), _uram_stats->free_memory + extram_stats->free_memory );
if (texttodraw>=1) azrp_draw_text(1,11, "PlayX = %d - PlayY = %d", MyPlayer.tileX, MyPlayer.tileY );
if (texttodraw>=1) azrp_draw_text(1,11, "PlayX = %d - PlayY = %d - VX = %d - VY = %d", MyPlayer.tileX, MyPlayer.tileY, MyPlayer.nextx.v - MyPlayer.currx.v, MyPlayer.nexty.v - MyPlayer.curry.v );
if (texttodraw>=2) azrp_draw_text(1,31, "Update = %.3f ms", (float) time_update / 1000.0f );
if (texttodraw>=2) azrp_draw_text(1,41, "Render = %.3f ms", (float) time_render / 1000.0f );
@ -301,8 +301,16 @@ int main(void)
azrp_circle( (int) MyPlayer.currx, (int) MyPlayer.curry, 8, C_RED );
azrp_line( (int) MyPlayer.currx-3, (int) MyPlayer.curry , (int) MyPlayer.currx+3, (int) MyPlayer.curry , C_GREEN );
azrp_line( (int) MyPlayer.currx , (int) MyPlayer.curry-3, (int) MyPlayer.currx , (int) MyPlayer.curry+3, C_GREEN );
azrp_line( (int) MyPlayer.nextx-3, (int) MyPlayer.nexty , (int) MyPlayer.nextx+3, (int) MyPlayer.nexty , C_BLUE );
azrp_line( (int) MyPlayer.nextx , (int) MyPlayer.nexty-3, (int) MyPlayer.nextx , (int) MyPlayer.nexty+3, C_BLUE );
azrp_update();
}
}
prof_leave(perf_render);
time_render = prof_time(perf_render);

View File

@ -56,13 +56,14 @@ void Player::Update( float dt )
this->vy += this->ay * DeltaTime;
this->vx += this->ax * DeltaTime;
if (this->vx >= MAXRUNSPEED) this->vx = MAXRUNSPEED;
if (this->vy >= MAXFALLSPEED) this->vy = MAXFALLSPEED;
//if (this->vx >= MAXRUNSPEED) this->vx = MAXRUNSPEED;
//if (this->vy >= MAXFALLSPEED) this->vy = MAXFALLSPEED;
this->nextx = this->currx + this->vx * DeltaTime;
this->nexty = this->curry + this->vy * DeltaTime;
if (MyLevel.CanGo( this ))
if (MyLevel.CanGo())
{
this->currx = this->nextx;
this->curry = this->nexty;

View File

@ -29,8 +29,8 @@ libnum::num32 sqrt_num32(libnum::num32 v)
Vector2D::Vector2D()
{
this->x = 0.0;
this->y = 0.0;
this->x = libnum::num32(0);
this->y = libnum::num32(0);
}
Vector2D::Vector2D( float x, float y )
@ -47,8 +47,7 @@ Vector2D::Vector2D( libnum::num32 x, libnum::num32 y )
Vector2D::~Vector2D()
{
this->x = 0.0;
this->y = 0.0;
}
Vector2D Vector2D::Clone( void )
@ -104,99 +103,6 @@ Vector2D Vector2D::Perp( void )
}
bool CollisionDroite(Point2D A,Point2D B, Circle C)
{
Vector2D u;
u.x = B.x - A.x;
u.y = B.y - A.y;
Vector2D AC;
AC.x = C.x - A.x;
AC.y = C.y - A.y;
libnum::num numerateur = u.x*AC.y - u.y*AC.x; // norme du vecteur v
if (numerateur <0)
numerateur = -numerateur ; // valeur absolue ; si c'est négatif, on prend l'opposé.
libnum::num denominateur = sqrt_num32(u.x*u.x + u.y*u.y); // norme de u
libnum::num CI = numerateur / denominateur;
if (CI < C.r)
return true;
else
return false;
}
bool CollisionSegment(Point2D A,Point2D B, Circle C)
{
if (CollisionDroite(A,B,C) == false)
return false; // si on ne touche pas la droite, on ne touchera jamais le segment
Vector2D AB,AC,BC;
AB.x = B.x - A.x;
AB.y = B.y - A.y;
AC.x = C.x - A.x;
AC.y = C.y - A.y;
BC.x = C.x - B.x;
BC.y = C.y - B.y;
libnum::num pscal1 = AB.x*AC.x + AB.y*AC.y; // produit scalaire
libnum::num pscal2 = (-AB.x)*BC.x + (-AB.y)*BC.y; // produit scalaire
if (pscal1 >= 0 && pscal2 >= 0)
return true; // I entre A et B, ok.
return false;
}
bool CollisionBorder(Border *B, Circle C)
{
return CollisionSegment( B->A, B->B, C );
}
Vector2D GetNormale(Point2D A,Point2D B,Point2D C)
{
Vector2D AC,u,N;
u.x = B.x - A.x;
u.y = B.y - A.y;
AC.x = C.x - A.x;
AC.y = C.y - A.y;
libnum::num parenthesis = u.x*AC.y-u.y*AC.x; // calcul une fois pour les deux
N.x = -u.y*(parenthesis);
N.y = u.x*(parenthesis);
// normalisons
libnum::num norme = sqrt_num32(N.x*N.x + N.y*N.y);
N.x/=norme;
N.y/=norme;
return N;
}
Point2D ProjectionI(Point2D A,Point2D B,Point2D C)
{
Vector2D u,AC;
u.x = B.x - A.x;
u.y = B.y - A.y;
AC.x = C.x - A.x;
AC.y = C.y - A.y;
libnum::num ti = (u.x*AC.x + u.y*AC.y)/(u.x*u.x + u.y*u.y);
Point2D I;
I.x = A.x + ti*u.x;
I.y = A.y + ti*u.y;
return I;
}
Vector2D ComputeVectorRebound(Vector2D v,Vector2D N)
{
Vector2D v2;
libnum::num pscal = (v.x*N.x + v.y*N.y);
v2.x = v.x -2*pscal*N.x;
v2.y = v.y -2*pscal*N.y;
return v2;
}
Border::Border()
{

View File

@ -13,8 +13,8 @@ class Point2D
Point2D() {};
~Point2D() {};
libnum::num x;
libnum::num y;
libnum::num32 x;
libnum::num32 y;
};
class Circle
@ -23,9 +23,9 @@ class Circle
Circle() {};
~Circle() {};
libnum::num x;
libnum::num y;
libnum::num r;
libnum::num32 x;
libnum::num32 y;
libnum::num32 r;
};
@ -122,12 +122,4 @@ class Border
uint16_t color;
};
bool CollisionDroite(Point2D A,Point2D B, Circle C);
bool CollisionSegment(Point2D A,Point2D B,Circle C);
bool CollisionBorder(Border* B, Circle C);
Vector2D GetNormale(Point2D A,Point2D B,Point2D C);
Point2D ProjectionI(Point2D A,Point2D B,Point2D C);
Vector2D ComputeVectorRebound(Vector2D v,Vector2D N);
#endif