Well, maybe this isn't quite a tutorial... or at least, I'll try to make it one!
I've posted a few movement systems before, but I have never really went in depth as to how I made them or the methods I use. I just provide a script... and that's not conductive to a learning environment. So here I will provide a script with lots of comments, and also go a little in-depth as to how I did things.
This movement isn't quite as barebones as previous ones I have posted. It does have things like some basic modes(pulling/pushing,swimming...), and then some(functions for replacing ganis, applications of things I've learned over the years).
First, I'm going to explain the method of wall collision I use. I use a simple dual-box check. This means I am checking two boxes in the direction the player is moving with onwall2's. This means it checks the whole distance of the player's size, and provides the necessary data for sliding(hitting one side of the player and not the other). Whenever I do this I sketch out a very easy diagram to understand what I am trying to accomplish.
I create two checks. CheckOne, CheckTwo. Each denote a box check in front of the player. There are important things to know.
1) the player's "true" size is 3x3 tiles, starting at... of course, player.x and player.y
2) However, the player is graphically starting at player.x+.5 and player.y
3) The collision box of the player is at player.x+.5 and player.y+1
4) The 'center' of the player is player.x+1.5 and player.y+2. This is where things like tile detection are done.
The above are all explained more easily in this picture, which also explains the box layout I use:

The white transparent box is where we determine the player's collisions should be. The blue outline shows what Graal consider's the full player's size.
Now each yellow and red box are checks that are determined in the script. However, only two are done at a time: only in the direction provided. This is not fully accurate, though... the boxes are not always 16x16 pixels(a single tile) in size. They stretch themselves outwards from the player to reflect the size of the player's speed. This means if the player is traveling to the left at 3tiles/frame, the box is going to be 3 tiles in width. This means it's checking for a wall between the new position and the last position(a simpler check means it will simply skip over walls found in-between).
Here is the script I use to generate the positions of the two checks:
PHP Code:
// CREATE CHECK POSITION FOR BOX ONE
temp.checkOne = {
player.x + .5 + vecx(k)*(k < 2 ? speed : 2),
player.y + 1 + vecy(k)*(k < 2 ? speed : 2)
};
// CREATE CHECK POSITION FOR BOX TWO
temp.checkTwo = {
player.x + 1.5 + vecx(k)*(k < 2 ? speed + 1 : 2 - 1),
player.y + 2 + vecy(k)*(k < 2 ? speed + 1 : 2 - 1)
};
For the first check, I initiate it at the upper-left of the collision box. If the player's direction is less than 2(to the left or up), move it outwards by their size. This is because when moving left or up, you want to displace the onwall2 check to account for the size it is going to be. This image representing a 3 speed movement can better explain:

The red + shows where the checkx/y is, and the red box represents the onwall2 check. Thus, if going to the left, it needs to be displaced 3 tiles to account for the fact onwall2 will be 3 tiles in width.
Anyways, for CheckTwo I start the check at the bottom right corner of the collision check. Then if the player is facing up or left, add a tile, or else subtract a tile(too keep it properly offset to represent the original sketch).
Now onto the wall checks itself:
PHP Code:
temp.hitwall = {
onwall2(checkOne[0],checkOne[1],(k in {1,3} ? speed : 1)-1/16,(k in {0,2} ? speed : 1)-1/16),
onwall2(checkTwo[0],checkTwo[1],(k in {1,3} ? speed : 1)-1/16,(k in {0,2} ? speed : 1)-1/16),
};
This might seem a little complicated at first, but it's actually very easy. Have an array that keeps track of the wall checks for CheckOne and CheckTwo. The positions, of course, represent the appropriate positions determined just above. The size of the onwall2 is figured out fairly easily. If moving left or right, make the horizontal check the size of the player's speed, otherwise make it the size of a tile. Do the same for up and down and moving vertically. Now one thing you'll notice is I subtract 1/16. 1/16 represents the size of a single pixel. Onwall2(x,y,1,1) is actually 17x17 pixels in size, for whatever reason. That means using just 1 will get the player stuck when they're not supposed to be, because it's checking an extra pixel(you'd get stuck trying to walk between two blocks of tiles with a 2-tile wide opening, because it would check a single pixel out of your collision box). Anyways, that bug is bad so we have to compensate.
Another important thing to do is if you DO hit a wall, account for the space between the player and the wall. If you're moving at 5tiles/frame, that means you can hit a wall way farther than your player actually is. If you just stop the player, chances are they will not be flush against the wall. Thus what we do is we run a simple loop. Now normally this is not fun, but if you take your above wall checks and make a function that will check in a given direction at the given speed, this becomes A LOT easier. Why? Because you're getting rid of redundant checks that you'd normally have to do, like checking for walls, and simplifying them. So what you do is you create a loop that starts at 1/16(explained in the script in comments), runs the length of the player's speed(to account for every pixel between the player and the wall), and increments at... 1/16! Again, this is to account for every pixel. Once you hit a wall, stop the function and use the data found(the length the loop ran), and move the player that much.
PHP Code:
temp.i = 0; // INITIATE 'i' OUTSIDE OF LOOP, SO IT IS ACCESSIBLE AFTERWARDS
for (temp.i=1/16;i<player.speed;i+=1/16) {
temp.CheckGap = CheckWall(k,i);
// IF WALL IS DETECTED, STOP THE LOOP TO RETAIN IMPORTANT PIXEL DATA
if (@CheckWall(k,i) != @{0,0}) break;
}
So just take 'i', and move the player in their direction that amount. What you have to do after though, is clean up the player's position. Normally doing checks like this will leave a player with some huge floating values(player.x = 5.45378234256464). So we do simple rounding for that:
PHP Code:
// IF DIRECTION IS LEFT OR RIGHT, MAKE CHANGES TO PLAYER'S X
if (k in {1,3}) {
player.x += vecx(k)*i; // ADD 'i' TO PLAYER'S X POSITION
// THEN GET RID OF DIRTY FLOATING VALUES
// AND ADD .5, SINCE THE PLAYER'S GANI X IS OFFSET .5 FROM POSITION
player.x = int(player.x)+.5;
// IF DIRECTION IS UP OR DOWN, MAKE CHANGES TO PLAYER'S Y
} else {
player.y += vecy(k)*i; // ADD 'i' TO PLAYER'S X POSITION
player.y = int(player.y+.5); // ROUND OFF PLAYER'S Y TO CLEAN FLOATING VALUE
}
As the first picture established, the player's visual position is actually .5x inwards. Thus, we round the player.x and THEN add .5. For player.y, no such thing is needed so we add .5 inside the integer function to round the player.y up to the nearest whole.
Sliding is quite simple with the two-box set-up. If only ONE box is hitting a wall, then you know your player should be sliding. What you do is simply use some determining factors to decide a direction for the player to slide... then do just what you would when moving the player. Check for a wall in the sliding direction, and if there is no wall slide them! I'll do my best to explain how I calculated the directions.
PHP Code:
// IF CHECK BOX ONE IS HITTING WALL, AND NOT BOX TWO...
if (player.wallcheck[0] == 1 && player.wallcheck[1] == 0) slidedir = (k + (k in {1,2} ? 1 : -1))%4;
// OTHERWISE ASSUME BOX TWO IS HITTING A WALL, AND NOT BOX ONE
else slidedir = (k + (k in {1,2} ? -1 : 1))%4;
If CheckOne is hitting a wall, and not CheckTwo is the first check we assume. Take player's direction. If player direction is LEFT or DOWN, add 1(turn counter-clockwise), otherwise subtract 1(clockwise). The %4 makes sure the player's direction is looped back around if it exceeds 3.
For the CheckTwo collission, it's somewhat similar except we are doing the opposite. If player's direction is LEFT or DOWN, subtract 1, otherwise add 1. I could have easily took all of this and made it into one big calculation, but for learning purposes I decided to keep it simple.
Then you just take the slide direction and use it!
PHP Code:
player.x += vecx(slidedir)*player.slidespeed;
player.y += vecy(slidedir)*player.slidespeed;
player.x = int(player.x/player.slidespeed)*player.slidespeed;
player.y = int(player.y/player.slidespeed)*player.slidespeed;
Now the last two lines are a bit important. What they do is similar to what we had to do after the wall collision. Because large floating values are bad for sliding(it can cause your player to get stuck, or 'stutter' when trying to slide into gaps), we have to get rid of them. What we do is round it to the player's sliding speed(I have it set to .25).
That pretty much accounts for movement and wall collision

However, things only get more complicated from here. You have to do various other checks to maintain the 'mode' of the player. This essentially reflects what the player's character is doing. Whether they're idle, walking, swimming, pulling...