🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Problem with the corner collision in platformer game

Started by
7 comments, last by Codeloader_Dev 3 years, 4 months ago

Hello, I have a small problem with my platformer collision system. I use SFML, C++ and Lua. Here is my code in lua to check collision between character and entity:

 collision=function(character,tile)

        local cLeft = character:getPositionX()
        local cRight =  character:getPositionX() + character:getBoundingRectWidth() 
        local cTop =  character:getPositionY() 
        local cBottom =  character:getPositionY() + character:getBoundingRectHeight() 

        local tLeft = tile:getPositionX()
        local tRight =  tile:getPositionX() + tile:getBoundingRectWidth()
        local tTop =  tile:getPositionY()
        local tBottom =  tile:getPositionY() + tile:getBoundingRectHeight() 

        --bottom collision
        if cTop <  tTop and cBottom < tBottom and cLeft < tRight and cRight > tLeft
        then
            character:setEntityPosition(character:getPositionX(),tTop - character:getBoundingRectHeight())
            character:setVelocityY(0)
            character:setOnGround(true)

        --top collision
        elseif cTop >  tTop and cBottom > tBottom and cLeft < tRight and cRight > tLeft
        then
            character:setEntityPosition(character:getPositionX(),tBottom)
            character:setVelocityY(0)
        end

        --right collision
        if cLeft < tLeft and cRight < tRight and cTop < tBottom and cBottom > tTop
        then
            character:setEntityPosition(tLeft - character:getBoundingRectWidth(),character:getPositionY())
            character:setVelocityX(0)

        --left collision
        elseif cLeft > tLeft and cRight > tRight and cTop < tBottom and cBottom > tTop
        then
            character:setEntityPosition(tRight,character:getPositionY())
            character:setVelocityX(0)
        end

    end,

The problem is when character touch a corner of the rectangle, then he intersects for example left and top side in the same time, and character is pushed to the wrong direction, it very confuse me, idk how to fix that, I was thinking about to add a vector variable that let collision know that character noves on x or y axis but in my platformer character can move in both in the same time, for example when player push W and D in the same time, do u know any solution?

Advertisement

You could use an average vector of these two collisions. But mainly its done like finding the closest collision (the one that occurs first move object back and check for collision again until none found) however there are collision jitters made by that approach, to focus on the topic more deeply you want to search for swept sphere collision

You are moving diagonally, but your code checked straight to the left or right, and then separately straight up and down, which are not places your object is going.

Honestly, ditch hspeed and vspeed.

You should check horizontally, then immediately manually move your object yourself before checking the vertical collision and then manually move your object again. Control is key.

jitterclicking is a great way to optimizing your clicking speed

The problem is that you are trying to fix collisions after the fact, when the objects are already intersecting. This can be made to work, but it's needlessly complicated in your case. Better to check for potential collision before your object actually moves, then clip the movement vector so that the intersection never occurs.

Programmist94sealand,

I would recommend updating your position code on a new temporary variable and then updating the character if, and only if the new position is valid. If it's not valid, then you can handle other cases you may want such as limiting movement right up to the edge of the collision detected wall.

Example (pseudo code):

// Note: Pointer to character so updates are saved back to the character object.  Could also return the new posion value and assign that way.

void UpdatePosition(obj &Character, obj TileMap, uint TimeStep)
{
	NewPos = Character->Position;
	NewPos.Update(TimeStep);
	
	if (IsValidPosition(NewPos, TileMap))
	{
		Character->Position = NewPos;
	}
	else
	{
		// Do other checking and movment attempts if needed.  
		// Such as setting the new pos right next to the wall, instead of inside the wall.
	}
}
void NewPos.Update(uint TimeStep)
{
	// Code to update position here.
}

bool IsValidPosition(vector2 NewPos, obj TileMap)
{
	// Check for collision here. Returns true or false.
}

Main problem is that you cache the bounding coordinates, then do the collision for the top/bottom; which may change the actual location.

Now you do checks for left/right, but do it with the OLD coordinates.

In my games, I have a DX, DY, and move it in one pixel steps in a loop until both DX and DY are zero or a direction is blocked. Handle collision when you'd actually run in a block, not afterwards.

DX, DY = delta x and y
while ( ( DX != 0 )
||      ( DY != 0 ) )
{
  if ( DX > 0 )
  {
    if ( CollisionOnTheRightSideWouldOccur() )
    {
      // handle collision
      DX = 0;
    }
    else
    {
      // move one pixel to the right
      ...
      // update delta      
      --DX;
    }
  }
  else if ( DX < 0 )
  {
    if ( CollisionOnTheLeftSideWouldOccur() )
    {
      // handle collision
      DX = 0;
    }
    else
    {
      // move one pixel to the left
      ... 
      // update delta
      ++DX;
    }
  }
  
  // same for DY
  ...
}

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

you can do it with while as endurion wrote. it will give the best results if the collision is not speed cirital.

you can also do with ifs to detect which direction your character is walking (keeping the results from the previous frame) and building the code around a lot of ifs. this will eat less cpu but its ugly to maintain, and will produce small bugs very rarely.

Hello,
This routine has saved my life:

    Detect_Collision: function(sprite, other) {
     var results = {
       left: false,
       top: false,
       right: false,
       bottom: false,
       center: false,
       left_corner: false,
       right_corner: false,
       x: 0,
       y: 0
     };
     var hmap_width = sprite.hit_map.right - sprite.hit_map.left + 1;
     var hmap_height = sprite.hit_map.bottom - sprite.hit_map.top + 1;
     var delta_x = Math.floor(hmap_width * (sprite.cdelta_x / 100));
     var delta_y = Math.floor(hmap_height * (sprite.cdelta_y / 100));
     // Create 12 collision points. The middle collision point is important.
     var t1 = { x: sprite.hit_map.left + delta_x, y: sprite.hit_map.top };
     var t2 = { x: sprite.hit_map.right - delta_x, y: sprite.hit_map.top };
     var tc = { x: sprite.hit_map.left + Math.floor(hmap_width / 2), y: sprite.hit_map.top };
     var l1 = { x: sprite.hit_map.left, y: sprite.hit_map.top + delta_y };
     var l2 = { x: sprite.hit_map.left, y: sprite.hit_map.bottom - delta_y };
     var lc = { x: sprite.hit_map.left, y: sprite.hit_map.top + Math.floor(hmap_height / 2) };
     var r1 = { x: sprite.hit_map.right, y: sprite.hit_map.top + delta_y };
     var r2 = { x: sprite.hit_map.right, y: sprite.hit_map.bottom - delta_y };
     var rc = { x: sprite.hit_map.right, y: sprite.hit_map.top + Math.floor(hmap_height / 2) };
     var b1 = { x: sprite.hit_map.left + delta_x, y: sprite.hit_map.bottom };
     var b2 = { x: sprite.hit_map.right - delta_x, y: sprite.hit_map.bottom };
     var bc = { x: sprite.hit_map.left + Math.floor(hmap_width / 2), y: sprite.hit_map.bottom };
     var bl = { x: sprite.hit_map.left, y: sprite.hit_map.bottom };
     var br = { x: sprite.hit_map.right, y: sprite.hit_map.bottom };
     // Determine which face was hit.
     if (self.Point_In_Box(t1, other.hit_map) || self.Point_In_Box(t2, other.hit_map) || self.Point_In_Box(tc, other.hit_map)) {
       results.top = true;
       results.center = self.Point_In_Box(tc, other.hit_map);
       results.y = other.y + Math.floor(other.height * other.size_y * other.scale);
     }
     if (self.Point_In_Box(l1, other.hit_map) || self.Point_In_Box(l2, other.hit_map) || self.Point_In_Box(lc, other.hit_map)) {
       results.left = true;
       results.center = self.Point_In_Box(lc, other.hit_map);
       results.x = other.x + Math.floor(other.width * other.size_x * other.scale);
     }
     if (self.Point_In_Box(r1, other.hit_map) || self.Point_In_Box(r2, other.hit_map) || self.Point_In_Box(rc, other.hit_map)) {
       results.right = true;
       results.center = self.Point_In_Box(rc, other.hit_map);
       results.x = other.x - Math.floor(sprite.width * sprite.size_x * sprite.scale);
     }
     if (self.Point_In_Box(b1, other.hit_map) || self.Point_In_Box(b2, other.hit_map) || self.Point_In_Box(bc, other.hit_map)) {
       results.bottom = true;
       results.center = self.Point_In_Box(bc, other.hit_map);
       results.y = other.y - Math.floor(sprite.height * sprite.size_y * sprite.scale);
       // Also detect bottom right and bottom left hit.
       results.left_corner = self.Point_In_Box(bl, other.hit_map);
       results.right_corner = self.Point_In_Box(br, other.hit_map);
     }
     return results;
   }

Codeloader - Free games, stories, and articles!
If you stare at a computer for 5 minutes you might be a nerdneck!
https://www.codeloader.dev

This topic is closed to new replies.

Advertisement