0
\$\begingroup\$

The character in my game constantly falls due to gravity, so i have to detect the collision between the bottom point of the character and the tile slope line on the same x coordinate.

The character must stop just on the slope floor. for that i also need to define where is the slope by storing x, y of both ends of the slope.

In the image bellow, you see a 32x32 tile map, origin of each image is top left.

playerPointX = 420;
leftSlopeX = 13*tileSize;
leftSlopeY = 11*tileSize;
rightSlopeX = leftSlopeX+tileSize;
rightSlopeY = leftSlopeY+tileSize;

so regarding the awnser i should write this ?

float run = playerPointX - leftSlopeX;
float rise = 1 * run;
float collisionHeight = leftSlopeY + rise;

and how i can detect the collision against collisionHeight ?

if (playerX<collisionHeight) // collision detected

or

if (playerY>collisionHeight) // collision detected

Finally when the collision will be detected should i use y=mx+b for playerY displacement ?

enter image description here

\$\endgroup\$
  • \$\begingroup\$ Can you please clarify how you're expressing the shape of the tile? What numbers do you store in your tile data to distinguish a steep up slope from a shallow one, or one that starts partway up a tile? \$\endgroup\$ – DMGregory Oct 8 '19 at 0:33
  • \$\begingroup\$ each tile is a struct who contain a variable that informs its type and in that case 2 is a slope tile, 1 = simple tile, 0 = sky \$\endgroup\$ – venom007 Oct 8 '19 at 0:43
  • \$\begingroup\$ Do you have only one kind of slope tile? In the image it looks like there are at least three different sloping tiles: one with a slope of +1, and two with a slope of +0.5, one making the bottom of the ramp and the other making the top end. If you have multiple slopes, or even just the distinction between sloping up and sloping down, how does your code differentiate these cases? \$\endgroup\$ – DMGregory Oct 8 '19 at 0:46
  • \$\begingroup\$ yes in my program i have only 45 degrees tiles, actually it depends what tileset image i use, i have tileset that contains 30 degrees, 70 etc and another just 45 degrees left, right \$\endgroup\$ – venom007 Oct 8 '19 at 0:53
  • \$\begingroup\$ I still need you to answer: how does your code tell the difference between a 30 degree tile and other slopes when they're present in the same tileset? What do you store that tells your code which slope to use for a particular tile? \$\endgroup\$ – DMGregory Oct 8 '19 at 0:55
2
\$\begingroup\$

It looks like all you need to do is find how far your character's center is shifted left or right from your line's anchor point, then compute how much your line rises over that interval (or falls, if the product of x offset and slope is negative)

float run = character.bottomCenter.x - line.position.x;

float rise = line.slope * run;

float collisionHeight = line.position.y + rise;
\$\endgroup\$
  • \$\begingroup\$ and how can I determine line.position.x in relation to player.x if we say that the tile is 32x32 size and coordinates are {300,200} ? \$\endgroup\$ – venom007 Oct 7 '19 at 22:48
  • \$\begingroup\$ Line.position just needs ro be some point on your line. Any point on the line will do. So, if your desired line passes through the bottom-left corner of your tile, then the bottom-left corner will suffice as line.position. \$\endgroup\$ – DMGregory Oct 7 '19 at 22:51
  • \$\begingroup\$ but before that I have to implement both ends of the slope for i get his position or angle ? (leftX,leftY) and (rightX,leftY) \$\endgroup\$ – venom007 Oct 7 '19 at 23:04
  • \$\begingroup\$ what is line.slope ? \$\endgroup\$ – venom007 Oct 7 '19 at 23:15
  • 1
    \$\begingroup\$ It's an uninteresting typo that has been corrected. \$\endgroup\$ – DMGregory Oct 25 '19 at 12:39
0
\$\begingroup\$

Well, to answer the letter of your question, a collision between a point and a line would require the point to literally be on the line, and due to floating-point rounding error, you will rarely ever see that in each frame by itself.

To detect a collision that has occurred, you would instead use a technique called sweeping to create a line segment from the point's location in its previous frame, to its coordinate in the current frame. The point of impact would then be trivial, as the intersection between two lines in 2D is simple. Here is an excerpt from Geometric Tools for Computer Graphics by Philip Schnieder and David Eberly (i've added some comments and changed some types for elaboration);

//Returns 0 if no intersection
//Returns 1 if there is a unique intersection
//Returns 2 if the lines are "the same" i.e. parallel and/or overlapping
//The variable I is used as output for the point of impact.
//P represents the origin of the line segments
//D is the non-normalized direction vector of the line extending from P
int FindIntersection(Vec2 P0, Vec2 D0, Vec2 P1, Vec2 D1, Vec2 & I, float squareTolerance)
{
  // Use a relative error test to test for parallelism. This effectively
  // is a threshold on the angle between D0 and D1. The threshold
  // parameter ’squareTolerance’ can be defined in this function or be
  // available globally.
  Vec2 E = P1 - P0;
  float kross = D0.x * D1.y - D0.y * D1.x;
  float sqrKross = kross * kross;
  float sqrLen0 = D0.x * D0.x + D0.y * D0.y;
  float sqrLen1 = D1.x * D1.x + D1.y * D1.y;
  if (sqrKross > squareTolerance  * sqrLen0 * sqrLen1) {
    // lines are not parallel
    float s = (E.x * D1.y - E.y * D1.x) / kross;
    I = P0 + s * D0; //I is now the point of impact
    return 1;
  }
  // lines are parallel
  float sqrLenE = E.x * E.x + E.y * E.y;
  kross = E.x * D0.y - E.y * D0.x;
  sqrKross = kross * kross;
  if (sqrKross > squareTolerance * sqrLen0 * sqrLenE) {
    // lines are different
    return 0;
  }
  return 2;
}

To answer the spirit of your question I would suggest instead of a point, think of your point a sphere with a very small radius. Now, still following the idea that you want to use a line segment to detect the collision between your point and segment without sweep testing, it becomes another trivial geometric problem. Simply find the distance from your point to the line segment, or better yet the squared distance, to avoid the square root entirely. Here's another excerpt;

float SquaredDistancePointToLineSegment(Vec2 TestPoint, Vec2 SegmentP0, Vec2 SegmentP1)
{
  Vec2 SegmentDirection = SegmentP1 - SegmentP0;

  Vec2 PointMinusP0 = TestPoint - SegmentP0;

  float t = dot(SegmentDirection, PointMinusP0);

  if (t <= 0)
  {
    // P0 is closest to TestPoint
    return dot(PointMinusP0, PointMinusP0);
  }

  float SegmentLengthSquared = dot(SegmentDirection, SegmentDirection);

  if (t >= SegmentLengthSquared)
  {
    // P1 is closest to TestPoint
    Vec2 PointMinusP1 = TestPoint - SegmentP1;
    return dot(PointMinusP1, PointMinusP1);
  }

  // closest Vec2 is interior to segment
  return dot(PointMinusP0, PointMinusP0) - t * t / SegmentLengthSquared;
} 

Now to see if your small sphere is colliding, simply check if the squared distance is less than the squared radius of your circle.

From the picture you've provided, my gut tells me that a more robust solution would be to think of it as a triangle, rather than a line. Using slopes and such is all well and good, but in my experience, solid polygons tend to be more robust. I highly recommend checking out Randy Gaul's 2D player controller educational github, as he has much to say about this exact question.

If you'd like to see simple triangle point collision detection, I recommend reading Christer Erickson's Realtime Collision Detection as it's a font of knowledge on this subject.

\$\endgroup\$
  • \$\begingroup\$ Ok thank you, i will study all of this informations \$\endgroup\$ – venom007 Oct 9 '19 at 5:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.