In SMB1 you always move the same amount (2 tiles) each time you press a button, so you could use 4 paths (up, down, left, right) for movement, but only move if there's no collision in that direction (e.g. place_meeting collision check at the coordinates obtained from path_get_x() / path_get_y() at the end of the path).
Also, only start moving if you're not already on a path. (You want to start the path in non-looping mode with the path end behavior set to "stop", relative to the current position instead of absolute)
Why would you use a path? That's overengineering it a bit, don't you think? I would understand for something like Super Mario World's map, since that uses more complex movements, but in Mario 3 and Shovel Knight, they both just move in straight cardinal directions until reaching the next node in the path. Using a path just to move in a straight line would be like using an excavator to dig a hole for a sapling.
EDIT: BTW, it's not always 2 tiles. You're forgetting this
The way I learned it years back would be to use a simple collision check and make your maps in the room editor. Make the sprite for the overworld player have a square collision mask the same size as your tileset. Both SMB3 and Shovel Knight use 16x16. Then, have a variable in the player object called
moving
. You use it to check if the player is already moving. You need to know this because otherwise the player can input any direction whenever and change direction with no regards to the map's limitations. In the events where you check for key presses to move, you will need something like this:
GML:
if (not moving) {
moving = true;
speed = 2; // Make sure the grid size is evenly divisible by the movement speed or checking for collisions will not work properly!
direction = 0; // Direction uses degrees. 0 is right, and each subsequent cardinal direction is +90 degrees counterclockwise
}
This prevents the player from being able to push buttons while they're still moving. Then, you need to set up your map nodes. SMB3 uses small coin icons as well as levels, while SK uses circles and levels. Every step, you check whether the player is currently overlapping a node and stop moving if so.
GML:
if (position_meeting(x, y, obj_node)) { // Make sure the node object's sprite also has a square collision mask the same size as the player's!
moving = false;
speed = 0;
}
...but wait! What if the player moves a direction that is invalid? You need to add some blocks that prevent invalid moves. They should just be invisible objects with a sprite that is--you guessed it--a square the same size as the player collision mask. If the player should only be able to move left and right, place them above and below the player. If the player can move up, left, and down, place one to the right of the player. And so on. Then, in the movement checking code, you need to amend it slightly:
GML:
if (not moving) {
// The following position meeting check assumes you are attempting to move right
// You will need to change it for checking different directions
// Left = (x - 16, y)
// Up = (x, y - 16)
// Down = (x, y + 16)
if (not position_meeting(x + 16, y, obj_block)) {
moving = true;
speed = 2;
direction = 0;
}
}