# Glitchy collision detection in platformer (solved)

This topic is 3744 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

This is about player vs. block collisions in a tile based 2d platformer. Currently it's using a 'shortest offset' method. If there's any overlap between the player and a block, the block assumes the player has collided with the closest edge, and then nudges the player in that direction, eliminating the overlap. The problem is that the collision often misfires. If, for instance, you are walking along a horizontal row of blocks, the blocks will sometimes decide you are colliding with their sides instead of their tops. Ick! Has anyone worked out a really solid alternate approach? For reference player velocities approach 1/2 tile per frame at times. [Edited by - Septagon on August 20, 2008 4:08:20 PM]

##### Share on other sites
Hi Septagon,
Perhaps the site below could help you. It is using Flash code, but I was able to get something working for a 2d platform game I had started making using Delphi (Object Pascal).

http://www.tonypa.pri.ee/tbw/tut12.html

Especially, see the "Hit the wall" and "Jumping" links on the left.

I hope this helps :-)

cheers,
Paul

##### Share on other sites
To make it a bit clearer, I have pasted the code my player sprite uses to do collisions with the surrounding tiles, etc. so you can get a better idea.

Procedure TSprite_Player.DoUpdate(Const ALevel     : TObject;                                  Const ATimeSlice : Single);Var    xtile        : Integer;    ytile        : Integer;    downY        : Integer;    upY          : Integer;    leftX        : Integer;    rightX       : Integer;    upleft       : Boolean;    downleft     : Boolean;    upright      : Boolean;    downright    : Boolean;    left         : Boolean;    right        : Boolean;    ldx,rdx      : Integer;    tdy,bdy      : Integer;    i            : Integer;    Platform     : TSprite_MovingPlatform;    MoveX        : Boolean;    MoveY        : Boolean;Begin    xtile := Math.floor(x / TLevel(ALevel).TileWidth);    ytile := Math.floor(y / TLevel(ALevel).TileHeight);    MoveX := False;    MoveY := False;    FTileSet.ImageList.GetImageCollisionDeltasByIndex(Frame,ldx,rdx,tdy,bdy);    // check against static solid tiles    GetCorners(ALevel,x,y + vely,downY,upY,leftX,rightX,upleft,downleft,upright,downright,left,right);    If vely > 0 Then    Begin        If upleft And upright Then        Begin            MoveY := True        End        Else        Begin            y    := (ytile + 1) * TLevel(ALevel).TileHeight - (height/2 - tdy) - 0.1;            vely := 0;            DoPlayerTileCollision(ALevel,Self,leftX,upY);            DoPlayerTileCollision(ALevel,Self,rightX,upY);        End;    End    Else    If vely < 0 Then    Begin        If Not Climbing And TileIsClimable(ALevel,Layer,xtile,downY) And                        Not TileIsClimable(ALevel,Layer,xtile,upY) Then        Begin            y       := ytile * TLevel(ALevel).TileHeight + (height/2 - bdy) + 0.1;            vely    := 0;            If Jumping Then Jumping := False;        End        Else        If downleft And downright Then            MoveY := True        Else        Begin            y := ytile * TLevel(ALevel).TileHeight + (height/2 - bdy) + 0.1;            vely    := 0;            If Jumping Then Jumping := False;            DoPlayerTileCollision(ALevel,Self,leftX,downY);            DoPlayerTileCollision(ALevel,Self,rightX,downY);        End;    End;    GetCorners(ALevel,x + velx,y,downY,upY,leftX,rightX,upleft,downleft,upright,downright,left,right);    If velx > 0 Then    Begin        If upright And downright And right Then            MoveX := True        Else        Begin            x := (xtile + 1) * TLevel(ALevel).TileWidth - (width/2 - rdx) - 0.1;            DoPlayerTileCollision(ALevel,Self,rightX,upY);            DoPlayerTileCollision(ALevel,Self,rightX,(upY + downY) Div 2);            DoPlayerTileCollision(ALevel,Self,rightX,downY);        End;    End    Else    If velx < 0 Then    Begin        If upleft And downleft And left Then            MoveX := True        Else        Begin            x := xtile * TLevel(ALevel).TileWidth + (width/2 - ldx);            DoPlayerTileCollision(ALevel,Self,leftX,upY);            DoPlayerTileCollision(ALevel,Self,leftX,(upY + downY) Div 2);            DoPlayerTileCollision(ALevel,Self,leftX,downY);        End;    End;    // check against moving platforms    If TLevel(ALevel).CollidedWithMovingPlatforms(Self,HitPlatforms) Then    Begin        For i := 0 To HitPlatforms.Count - 1 Do        Begin            Platform := TSprite_MovingPlatform(HitPlatforms.Items);            If (vely < 0) And (y > ((Platform.y - Platform.height/2) + Platform.height + (height/2 - bdy) + Platform.vely - 5)) Then            Begin                y       := (Platform.y - Platform.height/2) + Platform.height + (height/2 - bdy) + Platform.vely;                vely    := 0;                Jumping := False;            End;        End;    End;    If MoveY Then y := y + vely;    If MoveX Then x := x + velx;    If UseOtherFriction Then        velx := velx * OtherFriction    Else    Begin        If Running Then            velx := velx * RunFriction        Else            velx := velx * WalkFriction;    End;    If Climbing Then        vely := 0    Else        vely := vely + Gravity * ATimeSlice;    If vely < -TLevel(ALevel).TileHeight / 2 Then        vely := -TLevel(ALevel).TileHeight / 2    Else    If vely > TLevel(ALevel).TileHeight / 2 Then        vely := TLevel(ALevel).TileHeight / 2;End;

cheers,
Paul

##### Share on other sites
Thanks for the idea, Paul. The tutorial link has been bookmarked. I think I'll try the 'four corners' approach, and post back here when I've got it implemented.

##### Share on other sites
OK, problem solved! We didn't use the 'four corners' approach, but Paul's solution was helpful nonetheless.

The approach used combines two checks:
(1) smallest offset: are we overlapping with the block, and which side are we closest to?
(2) Check the result against the block's neighbors. Disable collision checks for sides that aren't exposed (because they face neighboring blocks).

Here's the code in case anyone else runs into the same issue (ActionScript 3):
	var blocked:Array = new Array(4);			blocked = main.GetBlocking(blockX, blockY);			sideHit = -1;			smallestOffset = 1;						// Left 			tempOverlap = right1 - left2;			// trace("Left overlap: " + tempOverlap);			if (tempOverlap <= 0)				colliding = false;			else if (tempOverlap > 0)			{				if (tempOverlap < smallestOffset)				{					if (blocked[2] == false)					{						sideHit = 2;						smallestOffset = tempOverlap;						correctedX = left2 - characterSizeX;						correctedY = characterY;					}				}			}						// Right			tempOverlap = right2 - left1;			// trace("Right overlap: " + tempOverlap);			if (tempOverlap <= 0)				colliding = false;			else if (tempOverlap > 0)			{				if (tempOverlap < smallestOffset)				{					if (blocked[0] == false)					{											sideHit = 0;						smallestOffset = tempOverlap; 						correctedX = right2 + characterSizeX;						correctedY = characterY;					}				}			}						// Top			tempOverlap = bottom1 - top2;			// trace("Top overlap: " + tempOverlap);			if (tempOverlap <= 0)				colliding = false;			else if (tempOverlap > 0)			{				if (tempOverlap < smallestOffset)				{					if (blocked[1] == false)					{						sideHit = 1;						smallestOffset = tempOverlap;						correctedX = characterX;						correctedY = top2 - characterSizeY;					}				}			}						// Bottom			tempOverlap = bottom2 - top1;			// trace("Bottom overlap: " + tempOverlap);			if (tempOverlap <= 0)				colliding = false;			else if (tempOverlap > 0)			{				if (tempOverlap < smallestOffset)				{					if (blocked[3] == false)					{						sideHit = 3;						smallestOffset = tempOverlap;						correctedX = characterX;						correctedY = bottom2 + characterSizeY;					}				}			}/* 			Note:			We're going to need a special case for blocks that have no 			open sides (surrounded by four blocks). */			if (sideHit == -1)			{				colliding = false; // Does that do it?			}			/* 			If 'colliding' is still true, our boxes are overlapping. 			We can use the offset to rectify this. */			if (colliding)			{				if (main.IsTop(sideHit))				{					vY = 0;					character.SetFreefall(false);				}				else if (main.IsSide(sideHit))				{					// Don't assume we're hitting sides when we clearly aren't!					vX = 0;				}				else if (main.IsBottom(sideHit))					vY = 0;				characterX = correctedX;				characterY = correctedY;				character.SetPositionX(characterX);				character.SetPositionY(characterY);				character.SetSpeedX(vX);				character.SetSpeedY(vY);			}

cheers,
Paul

1. 1
Rutin
70
2. 2
3. 3
4. 4
5. 5

• 21
• 10
• 33
• 20
• 9
• ### Forum Statistics

• Total Topics
633430
• Total Posts
3011826
• ### Who's Online (See full list)

There are no registered users currently online

×