• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Creating a World Map Similar to Shovel Knight or SMB3

J

jkocker18

Guest
Hello, I am starting a new video game similar to SMBs, and I was wondering what the best way to set up a level selection world where you can move from each level like a little map?

I can get a character to jump to the next selection, but I would like it to look like it is walking over there.

I have very limited code for this project as I was just starting today.

Thank you!

(Similar to These: )
 

Attachments

Yal

šŸ§ *penguin noises*
GMC Elder
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)
 
J

jkocker18

Guest
Thank you, I started reading up on paths as they are new to me, do you mean to create a new path each time a key is pressed if there is no collision that way?

So setting a variable = path_add(); and then having it move that move and the deleting the path with path_end();

Also how do I reference when the player is or isn't on a path?

Thank you
 
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 ;)
1614812136755.png

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;
    }
}
 
Last edited:
Yeah, but I figured it'd be easier to understand than state machines for a total newbie :p
It's just a single variable written in such a way that it's in plain English. If you can kludge your way through an overly-complicated solution using paths, you're more than capable of doing it the simpler, ideal way. Not to mention, still need to do collision detection like I suggest when blocking off invalid moves.
 
Last edited:

poliver

Member
Static and moving state.
Input only on static state (or possibly during the moving state as well if you want to be able to reverse directions).
Keep track of node you're in, possible directions from the node and distances to next node.
Run the moving state for long enough to cover the distance between the two nodes.

That's how I would do it.

The node data could be hardcoded or generated by checking the available paths and their lengths in all directions.
 
Last edited:

Locke

Member
@nacho_chicken
Love your simplistic code, and I followed it, but the issue I found is that when your player object moves over a node or a world object of the same size, itā€™s stuck and canā€™t move passed the object itā€™s on and over to another node or world. Rackā€™n my brain on how to get this to work. Any suggestions?
 

TheouAegis

Member
Or each node has a mask for which direction is valid, then move until you reach a node. That's the classic method. (not that objects blocking paths would be that heavy on a world map) Don't forget to snap to the node when done moving.
 

Locke

Member
Cool idea! How would you link the cardinal direction of the moving pathed object to know if the mask of a node is invalid or not once it comes into contact via a position_meeting ?
 

Yal

šŸ§ *penguin noises*
GMC Elder
Cool idea! How would you link the cardinal direction of the moving pathed object to know if the mask of a node is invalid or not once it comes into contact via a position_meeting ?
The classic way is using the dot product of the two directional vectors - if it's less than zero, they're going in opposite directions (0.5 means they're at a 45 degree angle, 0 means they're perpendicular, -1 means they're 180 degrees away from each other)
 
Top