Colliding a platformer character with the ground

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Colliding a platformer character with the ground
by on (#193615)
So I want to have a platform game, and of course, I need to program a way for the game to know wether or not the main character is touching the ground or wether it's walking on a ramp or not. I plan on having it get the X-position of the ground once it is in the area visible on the screen, and it will judge this based on the screen number and the scroll settings, but I realize this will take a whole lot of ROM space. Is there a simpler way of doing this that doesn't take so much ROM space?
Re: How would I do this?
by on (#193705)
I would suggest you store your level data in a compressed form, so that you save ROM space. And once you extract the data to display it on the screen, you should put some values into an array in RAM that tells you what the current part of the level looks like. And then you can calculate your character's position from there.

For example, in my game, I had an array with 33 entries: One for each of the visible horizontal tile on the screen. (If the scrolling isn't aligned to eight pixels, you see 33 tiles instead of 32, with the first and the last only being partly visible.)

Each value contained the height of the current platform.

Whenever the scrolling reached eight pixels, I shifted the values in the array to the left and loaded a new item at the end.

This way I was able to calculate walls and platforms for the characters.

For more complicated levels, you might need a two-dimensional array with width and height.
If all of your items on the screen are aligned to 16 pixels instead of 8, arrays with a width of 17 instead of 33 would be enough.
Re: How would I do this?
by on (#193709)
So you have a screen map somewhere that holds the what tiles go where right? maybe you have chars, maybe you have "blocks".
Let say you have chars.
CollideChar = map[PlayerX/8 + (( PlayerY/8)*32)]
As you can see on a NES this will work out very nicely with bitshifts and AND and an OR ;)
So now you just index into your Tile map and read the value of the tile. Sort you tiles like so

0-A-B-C-D
Where A is the last empty tile ( pure background ), B is the last can pass through but walk on, C is the last solid, D is then your Special chars. Or you might want to flip it around or any other order. But basically you then get a
load char
< A then ignore
<B then stand on it/pass through it
<C stop
-C, index into special char ( like slope) and then react accordingly .

If you have blocks that are 4x4, then you do a /32 instead of /8 then you get the block number, then you work out which char you are in the block by doing /8 AND 3 on the X and Y etc

You will also need to add/subtract a Scroll offset to get the Screen coordinate into "map" space but that should be trivial enough.
Re: How would I do this?
by on (#193710)
There are many different ways to compress level maps, and several things have an impact on which method suits your game the best. For example:

- Do levels scroll? If so, in one axis or both?

Levels without scrolling are the easiest, because you can usually store them in the ROM using whatever compression format you see fit, and just decompress each screen to RAM and use only that from then on. Games that scroll may still buffer the active portion in RAM, but since more than one screen is active at any time due to scrolling, more RAM will be needed, and the compression format needs to be compatible with the size of the RAM buffer (e.g. compressing entire screens into binary blobs is not a good idea if your buffer can't hold a whole number - 2 or more - of screens... in that case, it would be better to compress levels as rows or columns, for example). Random access to compressed maps becomes harder when the game scrolls in both axes.

- Are levels destructible (i.e. can blocks be removed/added dynamically during gameplay)?

Small changes to the level map can usually be implemented using objects, but when hundreds to thousands of blocks can be changed, there's no other way than putting the entire level map in RAM. Since the NES has only 2KB of RAM, that pretty much mandates that extra RAM be present on the cartridge (typically 8KB, but more is possible). Using this method you can decompress levels stored in any format you see fit to RAM in their entirety, and modify them at will.

- How big are the building blocks of your levels?

A full, uncompressed screen (minus patterns, of course) is 1024 bytes on the NES. That's a lot of memory, but games hardly need 8x8-pixel precision when displaying their backgrounds. Most NES games are built out of 16x16 blocks (i.e. metatiles), in part because that's the resolution for color attributes. That step alone can cut the size of a screen from 1024 bytes down to 240 bytes, at the cost of a separate table containing definitions for up to 256 metatiles, with at least 5 bytes per entry (4 tile indices + 1 byte containing the palette index and collision information), which can (and must, otherwise there'd be no compression!) be shared across many screens. Some games (specially games from Capcom, such as Mega Man, Duck Tales, The Little Mermaid, and so on) create levels using even larger blocks: 32x32 pixels. This not only reduces the space required by each screen to 64 bytes (that's only 6.25% of an uncompressed screen!), but it also makes working with color attributes much easier.

This is all that I can think about this matter right now. There's no single answer that fits all games when it comes to level compression, you have to take a hard look at your specific situation and come up with something that's a good compromise between complexity and size, considering how your engine will have to deal with the data and how much ROM space you have to spare.
Re: How would I do this?
by on (#193713)
Oziphantom wrote:
You will also need to add/subtract a Scroll offset to get the Screen coordinate into "map" space but that should be trivial enough.

I personally find it much cleaner to do the opposite: have everything working in map space, and subtract the scroll to convert coordinates into screen space, for drawing sprites and calculating the target addresses for tile/attribute updates. This is more compatible with the development methodology of separating the model (simulated world) from the view (visual representation of the simulation).

The idea is that your game exists and functions even if nothing is rendered to the screen, since the video is just a representation of what's happening in the game so players are able see what's going on and control their characters, but it's not THE game. There are a number of advantages in doing things like this, such as portability to other platforms. If your game world functions independently from the video hardware, most of the game engine can be ported to other systems without any modifications, while the view's logic will go through heavier refactoring to suit the new hardware (resolution, number of sprites, sprite sizes, number of colors, and so on). Another advantage is that you can create different views of the same world. You can render the same game either as a side scroller or as a first person shooter (where you can only move forward of backward, but still), you just need to code different views that interpret the world differently (think of how in Duke Nukem 3D you can play using the 3D view, a top to bottom view of the map, or a combination of both). You can even use multiple instances of the same type of view, for split-screen multiplayer, for example, something you wouldn't be able to do if objects were anchored to a single screen.

All these possibilities might seem far-fetched for a platform as weak as the NES, but you never know if you're ever going to port your games to modern consoles, smartphones, or anything more modern that is able to process different/multiple views. Plus, it's just good practice, that will teach you something you can use later on in situations where this might mean more than it does in NES development.
Re: How would I do this?
by on (#193762)
Could you explain tokumarus example in simpler terms? I'm not a computer noob, I just think of computers differently than most people.
Re: How would I do this?
by on (#193764)
The object has a position in the world, independent of what cameras are looking at the object. You store the object's position relative to the edges of the world. You do not store the object's position relative to the position of the camera.

The camera is an object. Like other objects, it has a position in the world.

You do all your collision detection based on the object's position in the world. The camera ideally has nothing to do with it.*

When you render the screen, such as by drawing sprites to a display list to be copied to OAM, you calculate the position of each object relative to the camera each frame by subtracting the position of the camera object from the position of each other object. The display list is the only place you store the position of each object relative to the camera, and even then, for no longer than one frame at a time.


* Advanced technique: You can use an object distance from the camera position to "cull" objects, or not perform computationally expensive processing on them. One kind of culling is removal from the list of active objects.
Re: How would I do this?
by on (#193785)
The way I plan on doing it is once a ground is visible on the screen, it will get its starting X-position and ending X-Position, And then it will compare that to the players X-Position as well as compare the Y-positions. But this method will eat up a lot of ROM space because I'd have to program hit detection for each ground surface in the entire game, and that's very impractical.
Re: How would I do this?
by on (#193794)
Quote:
The way I plan on doing it is once a ground is visible on the screen, it will get its starting X-position and ending X-Position, And then it will compare that to the players X-Position as well as compare the Y-positions. But this method will eat up a lot of ROM space because I'd have to program hit detection for each ground surface in the entire game, and that's very impractical.

You need to be precise when programming, and there was a lot of hand waving there.

Quote:
The way I plan on doing it is once a ground is visible on the screen

What do you mean, "a" ground? A ground tile? An object that represents a single platform? You need to figure out how, precisely, you're going to represent your maps as data - how you do collision detection with it falls directly from that. And for us to give you suggestions about how to store your maps, you need to tell us more - anything! - about the nature of them and your game. There really isn't a one-size-fits-all solution, especially on a limited 8-bit platform.

Quote:
it will get its starting X-position and ending X-Position

What will? Your game engine? How does it get that data? How often does it do this check?

Quote:
But this method will eat up a lot of ROM space because I'd have to program hit detection for each ground surface in the entire game

Again, it's not clear what you mean here. What is a "ground surface" in this context? You have something in mind, but you're too vague on the details for us to help you.

I'm hesitant to try to actually answer your question, because it feels like a case of giving a man a fish instead of teaching him to do it (and when he only has a vague idea of what water or a fishing rod are). But FWIW, say you're making a Zelda-like overhead game with no scrolling; you might store your map as a grid of 16 pixel by 16 pixel tile types. The NES has a 256x240 screen, so that'd be a 16 tile by 15 tile screen (because 256 divided by 16 = 16, and 240 divided by 16 = 15). You could assign each type of tile a number: grass might be 0, dirt road is 1, rock is 2, etc.

Your character's position on screen would be stored in pixels; to keep things simple, say it's a dot rather than a square. You'll probably want to figure out what tile they're on; to convert from pixels to tiles, you'd just divide by 16 and throw away the remainder. So if they're at X=148, Y=63 on screen, they're on the tile located at X=9, Y=3 in the map.

Conveniently, on old processors like the 6502, even though general purpose division is hard, division by a power of 2, like 16, is easy - every right shift divides a number by 2, so if you right shift four times, you'll divide by 2^4, or 16.

Now here's a question: when do you want to check for collisions? I'd say, maybe, whenever you try to move the character, you want to see if the new coordinates you're trying to move to can be walked on. So let's say the player is trying to move down one pixel, the new Y coordinate is 64 in pixel space, or 4 in tile space. So you'll want to check the map to see what kind of tile that is. How can you do that?

Well, let's assume you're storing your map as tile numbers from left to right, top to bottom. If Y were zero, how would figure out which number to look at? That's right, it'd just be the X tile coordinate. Now suppose X is 0, but Y is 1 - which tile do you check now? It'd be the first tile in the second row, which would be tile number 16. In general, to compute an index into your tile list, you'd calculate Ytile * the width of the map in tiles + Xtile. You can used indexed addressing to look a computed number up in a list of numbers like the tile map.

Now once you have the tile number, what do you do? Well, you have a bunch of options, but in the simplest case you can store a list mapping tile types (numbers) to a number that indicates whether or not you're allowed to walk on it - 0 means you can, 1 means you can't. Then you just use indexed addressing again to look up whether the tile is walkable or not, and behave accordingly.
Re: How would I do this?
by on (#193797)
. What I meant by beginning X-Position and ending X-Position was a trick I'm using to save RAM. How it works is that it will get X-Position of the leftmost ground tile and then get the X-Position of the rightmost tile. Once it gets those, it will check if the Players X-Position is greater the leftmost tile and less then the rightmost tile.
Re: How would I do this?
by on (#193814)
So you're using run-length encoding on platform widths. I used a similar technique in Haunted: Halloween '85 and its sequel: the floor is a run-length encoded heightmap, and platforms are rectangular slabs with a top left corner, width, and height.

(May I change this topic's subject to something more descriptive?)
Re: How would I do this?
by on (#193823)
tepples wrote:


(May I change this topic's subject to something more descriptive?)

Sure, go ahead