A common way I've used is to store two variables for each of the position components (X, & Y). One old, the other, new. So that one of the X variables stores the old X position, and the other variable stores the current position, and likewise for the Y coordinates. If a collision on the tile at the current X & Y coordinates is detected, then it will restore the current X & Y coordinates to the old ones, and either negate or reverse the X & Y velocities, to either stop the movement or to simulate a deflection effect.
That method I used is rather rudimentary.
I currently employ better version of it, where if the distance between the old position and the new position is greater than the tile size, it performs a linear check of all the tiles in between by finding the slope of the player's path and recursively checking each of the tiles along that slope.
'Where S is the tile size (each tile is square) 'Where X0 is the old coordinate, and X1 the new ... Likewise for Y0 & Y1. '// If the distance travelled was greater across the X-axis ... XD = (X0 DIV S) - (X1 DIV S) YD = (Y0 DIV S) - (Y1 DIV S) FOR I = 0 TO XD STEP SGN(XD) IF COLCHK( I , (I DIV ABS(XD))*SGN(YD) ) THEN 'Enact collision NEXT I '// If the distance was greater on the Y-axis, do the same except using Y-coordinates instead DEF COLCHK( X , Y ) 'Define collision ENDIt is worth noting that you ought to check the collision at the points along the slope PLUS the displacement of the player, since XD & YD are simply distance, not coordinates, and do not account for the offset of the player within your game world. Since the code will not store the old X & Y coordinate values if they belong inside a colliding tile, it shouldn't be possible for both variables to be at fault, and cause the player to be stuck within a tile.