When you have a game where the maps goes only from left to right and the exit is always at the end then it is quite easy to manage the exit point (end of the map) or the bottomless bit (other holes in the map) but what if your map would allow to exit at multiple locations, either by falling through a hole in the middle of the current map and ending up in another one, having some possible ladder that either goes up or down which causes multiple branching point and bottomless pits?
For now I'm not sure yet where and how to manage it so before starting to implement, I thought it would be a good idea to listen to other people opinion before making some mess.
For now, one possible way could be when processing the current frame:
- while processing user input, if actor goes out of bound (up/down of screen or begin/end of current map), raise a flag
- later during the frame processing, if flag out of bound, check location
? this is where I'm not sure "what" location means:
- current screen in the map (1 screen is 256 pixel), check if exited up/down/left/right and react on that
- x, y position again possible hitboxes (seems intensive)
- have a list of exit point and check somehow against them (not sure what and how yet, seems similar to hitbox)
Maybe there is something even easier, it just that I don't see it yet. I write these days the logic in C so it's a lot easier to visualize so idea in either C or asm and why it is better that way will be really appreciated.
In
The Curse of Possum Hollow, each map has a list of exit points. Each exit point has a source (in 16x16 pixel units), a destination map ID, and a destination position. When one is on screen, the engine checks to see if the player is inside it.
In addition, in both
Curse and its predecessor
Haunted: Halloween '85, each map has a side, top, or bottom exit and an exit size. Touching a full-height wall at the right side activates the side exit. Approaching the top or bottom of the screen activates a top or bottom exit, so long as the player is close enough to the end of the map. (If not, it's processed as an ordinary ceiling or pit.)
Here's the exit logic that both
Haunted games use, roughly translated into C:
Code:
// These are set to 0 at the start of every map
unsigned char boss_running = 0;
unsigned char want_exit = 0;
// This is in the main loop
void check_exit() {
switch (level.exit_type) {
case EXIT_RIGHT:
if (actor_x[0] >= map_width - 8) {
want_exit = true;
}
break;
case EXIT_TOP:
if (actor_x[0] >= map_width - level.exit_argument
&& actor_y[0] < 16) {
want_exit = true;
}
break;
case EXIT_BOTTOM:
if (actor_x[0] >= map_width - level.exit_argument
&& actor_y[0] >= level.pit_height - 16) {
want_exit = true;
}
break;
case EXIT_BOSS:
if (actor_x[0] >= map_width - 160
&& !boss_running) {
boss_running = level.exit_argument;
boss_setup_procs[boss_running]();
}
break;
}
// Omitted: Scan the level's exit point list
}
// The main loop also calls this.
// During a boss, its run procedure will set want_exit
// once the boss is defeated.
if (boss_running) {
boss_run_procs[boss_running]();
}
That gives me some ideas, thank you for the pseudo-code.
This mean in your case that if you map has multiple pits, any pits becomes "the bottom exit" case. What if you have one case where you have a ladder that goes at the bottom after the pits. Since your example show the bottom exit tested first, would the exit list still be tested later even thought the flag was set? Or because the flag was set, it would check if the exit exist in the list to know which one it is? (and unknown one would means a bottomless pit, I guess?)
Banshaku wrote:
This mean in your case that if you map has multiple pits, any pits becomes "the bottom exit" case. What if you have one case where you have a ladder that goes at the bottom after the pits.
In top and bottom exits,
level.exit_argument is a width value that distinguishes the ladder from the pits. If the ladder is 64 pixels from the right side of the map, and the pits are 128 pixels from the right side of the map, set it to 96 so that the
actor_x[0] >= map_width - level.exit_argument test distinguishes the two.
Whenever I need metatiles to have special behaviors that need extra parameters, I flag them as having the specific behavior (in this case there would be an "exit" flag somewhere in each metatile's definition), and whenever there's a collision with them I look them up in a list of special behavior blocks sorted by coordinate, where I can find the remaining parameters to execute whatever is that the block does (in this case, the target location). If the list is long, I use binary search instead of comparing element by element.
@tepples
The part that confuse me is when you say "from the right side of the map", like either the map represent 1 screen (8 bit value) and there is an exit in that current location or there is only 1 exit from the top from this 16 bit value. Then later other exit point are confirmed from a list. You example gives me a lot of ideas to start make my own, I just want to make sure I understand your example properly.
@Tokumaru
I was thinking about that too, making a metatile with a special attribute for the exit point. My current issue with that is that is even though I would like to implement it, my current test code from 10 years ago was using some kind of ideas of the top 4 bits was for the color for the metatile and the bottom 4 bits are used for some flag. The issue with that is that it reduce the amount of possible flags to 16 values only, which could be enough. If not, I would need to separate the extra attribute per metatile in its own byte but that would increase the size and require to edit it either manually or updating the code of the map editor I didn't touch in 10 years. The other thing is that it would increase the count of metatile for special case like these per screen. Maybe this is not an issue, need to test it first. I will keep that idea in mind, thanks
Another thing I see in Tepple example is "exiting" the screen doesn't mean to be out of bound ( y < 0 or > 240) from an hardware point of view but more from a specified logical boundary. For action game similar to mega man, ninja gaiden etc maybe this would be fine too, which is what I hope to be able to make with such an engine. I don't know why I was so much set on an hardware boundary. I will see how much impact it as with a different offset.
Banshaku wrote:
The part that confuse me is when you say "from the right side of the map", like either the map represent 1 screen (8 bit value) and there is an exit in that current location or there is only 1 exit from the top from this 16 bit value. Then later other exit point are confirmed from a list. You example gives me a lot of ideas to start make my own, I just want to make sure I understand your example properly.
There are two different exit systems. One is based on reaching the end of the map, which can be up to 8192 by 240 pixels in size. A map can have only one exit using that method. All secondary exits use the 16x16-pixel exit points.
I like give each block type an effect field. Could be "do nothing", "pick up coin", "finish level", "go up one room", "go down one room"... it's just a jump table, stored in parallel to attributes like solid/water/air/etc, and triggered whenever the player (or even a monster if I want that) touches a block of the coresponding type.
They're arbitrary code, so they can read data from elsewhere, like "number of room above", "last saved overworld map location", "is player holding key", "is player on ladder" and so on as needed. They can even be completely generic, jumping through a second per-level table or toggling multipurpose flags that can be used by other 'scripts'. Standing on a block with the "toggle flag 1" effect might open a door, trigger a trap, spawn a boss or even all three, depending on what reads that flag in the current level.
That's on PC though, I guess the overhead might be a problem on the NES. I'd probably still use it anyway, just for the flexibility.
If you want to make different bottomless pits lead to different places, you can simply have a list specifying the X coordinates where the destination changes, and what the destinations are (could even be death, despite the fact that making mortal dangers indistinguishable from passageways isn't a great game design choice). Then, when the player goes out of bounds, scan the list and stop when you find an X coordinate greater than the player's, and the destination before that is the one you should use.
If you want exits that are not out of the level's bounds, you can simply create an "exit" object, which can have the dimensions you need. If your object definitions are anything like mine, you'll have a parameter byte that each object type uses for a different purpose. For the "exit" object, this parameter could be the destination, if it fits in 8-bits, or the index of a more complex entry in a table of exits.
My current test maps maybe don't have anything complicated yet so I think I can get away with simple testing of location for the first phase until I define my real game. I just did some quick hack and was able to go from maps to maps based on everyone comments. Now I need to write the real thing. For the first testing, an out of bound check based on direction (up/down/left/right) and x location was just enough to do the job.
I think it is very uncommon for game like mega man to have bottomless pit and exit point pit in the same map so I guess that case was maybe overthinking and bad design, like you mentioned.
It's my impression that classic 8 bit games with this mechanic usually do it in one of two ways, depending on which makes sense for their game design:
1. Store your world/level data in a way that the game engine is able to automatically calculate which "room", etc. to load up depending on where you leave the previous one. Ie. if you fall through the floor of room A with an X coordinate of 729, you'll automatically spawn in the top of room B with the same X coordinate. This allows for really dynamic map design that makes more sense for the player, and it saves a lot of work defining exit and entry points.
Obviously there's a lot of overhead to this, and it requires you to design your game in a very specific way. It's definitely overkill if your game isn't a large sprawling metroidvania from the get-go. To get around the limitations, it might even be a good idea to combine with method #2
2. Simply define the exit point by coordinate limits. Eg. coordinate 0+: Room X, coordinate 600+: Room Y, etc. Though they probably exist, and it's definitely common in modern games, I don't recall seing any game of the NES era treating exits outside the bounderies of the level as "objects" in the level. (but if you have stuff like doors in the background they of course are)
Super Mario Bros. has a really primitive implitation of this mechanic, which is the reason for famous glitches like the minus world, or the green pipe to the 4-2 warp zone that speedrunners use. The exit location simply changes when the scoll coordinate progresses to a specific coordinate.
There is basically one method.
Volumes. You put down a "volume" and detect that the player entered it then warp as needed.
If you make separate AABB collision volumes and use them.
If you make Special collision data flags
If you make custom meta tiles that do it
Its all the same thing, just implementation details.
If you are limited to flags at 16. Do you have more than 16 per screen or "area". You can make them index into a "warp coords for this screen/map/node/portal/area"
If you don't use all your "metatiles/blocks" then you can say have F0 F1 F2 FX where X is the warp index etc
I think the trick with the metatile is an interesting one. I will keep that in mind. thanks!
If I wanted something quick, I'd use the object system for it (assuming you have one in place). Simply make an invisible object that checks for collisions against the player.
Depending on your game you might not want to waste an object slot for this though.
For now the only entity that is managed is the main actor so no system is in place yet. Now that I'm getting close to a current milestone (transition between maps and basic but not perfect movement of main actor) I need to start to implement such system so I will keep that idea in mind too. Thanks!
thefox wrote:
If I wanted something quick, I'd use the object system for it (assuming you have one in place). Simply make an invisible object that checks for collisions against the player.
Depending on your game you might not want to waste an object slot for this though.
I used this method for my entire game.
That's kind of surprising, considering your game fits really neatly into one huge grid.
rainwarrior wrote:
thefox wrote:
If I wanted something quick, I'd use the object system for it (assuming you have one in place). Simply make an invisible object that checks for collisions against the player.
Depending on your game you might not want to waste an object slot for this though.
I used this method for my entire game.
FWIW, I also used this approach for STREEMERZ (mainly because it was used by the original game as well
).
Sumez wrote:
That's kind of surprising, considering your game fits really neatly into one huge grid.
Well, it mostly does but not entirely. I specifically wanted to have areas that don't connect in a straightforward 2D grid (think "lost woods"), so the room links are arbitrary. There's also a room where the edges wrap, so that had to be representable as well.
Maybe to give a little more detail, there's a few kinds of objects for moving from room to room:
- pass: touch this and you go to another room)
- door: touch this while holding up and you go to another room, character facing will flip to represent being on the other "side" of the world)
- horizontal pass: 8 pixels wide, full screen tall, takes you to another room but velocity and Y position is preserved (normally placed at an edge)
- vertical pass: two screens wide, 8 pixels tall, takes you to another room but velocity and X position is preserved (normally placed at an edge)
Every game object has a "parameter" byte. For these it's an index into a list of 8 connecting rooms (part of the data for each room).
In the data for the editor every room has a position on the grid, but this is not exported because the game engine itself doesn't use that information. It's only to help me organize it and pick rooms from a map view. The tool has a button to automatically create the 4 objects and connections to the 4 map-adjacent rooms, but these can be removed or replaced, or linked to other rooms arbitrarily. A lot of rooms have a wall on one side, etc. that might let me free up a slot to use for another object.
Parallel to the list of 8 connecting rooms is a list of 8 X,Y entrance coordinates. This is used when entering the room, the coordinate used depends on which
exit was taken in the previous room. If arriving by pass/door the player just takes on that coordinate, if it's the vertical/horizontal pass type it only sets one of the coordinates and retains the other.
There's probably other variations of this in there, and some weird rules to do with 1-screen vs 2-screen rooms, but this is the basic idea. It was pretty simple, and it didn't really feel like it had much impact on the object budget or performance (rooms with all 16 objects were rare, it usually seemed crowded to have that much stuff).
Dunno if I'd do other games the same way, but it was convenient for this one. If it was a game with big streaming rooms where objects come and go it'd probably need some "priority" concept so that doors and stuff can't get despawned... but for a different game I'd make a different evaulation about how to do this. This question is very much in the category of "game-specific", IMO.
@thefox
STREEMERZ... I have good memories of it
One anecdote about it is at the time, I was playing games on the train with an emulator on the psp. When I first tried STREEMERZ, the music for the intro was "unusual", with high pitch and broken notes and I thought : "man, that's really makes it feels like some kind of action 52 game pack!" and had a blast. Later, I figured out that the broken music was not by design but the emulator didn't support illegal instructions, thus the broken intro music. Still, I like that version
@rainwarrior
That's a lot of interesting way to connect room, I will take notes of it. Right now, to debug the transition code between maps, since the amount is limited to 10~13 in my current samples and more or less goes from left to right (sometime connecting top or bottom), I just did a debug method which is called when the main actor is out of bound.
There is a switch that check what to do based on the map number. For example, when you are in map "0", if you go out of bound "up" and where in ladder mode and in the last screen of the map (based on actor X location), you go map 1. For "1", if out of bound and you go up ladder then go map 3, if falling down or ladder go back to 0 and adjust location of actor to be at end of screen so screens will be buffered properly, etc.
For now, this is quite crude but I think I could create some kind of lookup table that would tell what to do based on the map and this could be reused for other stages. But this is more than enough for debugging purpose and will refactor the code later.
After 10 years, to be able to transition between maps and the engine is starting to buffer the screen appropriately is a big enough milestone that I don't mind the debug code since once compiled, it doesn't matter much as long that it works, isn't it?