How Do You Code BG Collision?

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
How Do You Code BG Collision?
by on (#164649)
I decided that I wouldn't get myself stuck like last time messing with object animation and vram slots and whatnot, so I've decided that I want to try something with BGs. The thing is, how does it work? Isn't it like you load the object's x position, divide it by the width of each block to find the starting block, and also divide the hitbox width by the width of each block to find how many tiles over you look for? You'd also do this vertically. Then, based on your new offsets (you'd record the stuff you found), you'd look over every one of the blocks and see how it effects the object and then do that, like if it's a wall, it will push the object out of it. I kind of wonder how collision maps are generally stored. I'd probably do a byte per block. This way, when you're dividing the object's position and width/height, you can store that number into x or y and not do anything else and you'll be able to offset the collision table exactly where you want to.
Re: How Do You Code BG Collision?
by on (#164651)
Espozo wrote:
Isn't it like you load the object's x position, divide it by the width of each block to find the starting block, and also divide the hitbox width by the width of each block to find how many tiles over you look for?

To avoid getting touching the subject of how hitboxes are defined (some people like them at the top left corner, others in the center, etc.), it might be better to say that you check all blocks from one corner of the hitbox to another. For example, to look for collisions against the ground when falling, you'd check all blocks between (left, bottom) and (right, bottom). And yes, coordinates must be divided by the dimensions of the blocks that define the collision map.

If the game is only made of blocks, this is all it takes to do proper collision detection: move horizontally, check for solid blocks in the direction of the movement, eject if solid blocks are found; move vertically, check for solid blocks in the direction of the movement, eject if solid blocks are found.

If you have slopes though, things can get quite a bit more complicated, and there are hundreds of ways to implement those depending on the kind of levels you want to make. Generally, slopes affect the speed of objects (speeding them up or down depending on the angle and the direction of the movement), and after being moved horizontally, they "snap" to the ground. The exact way in which an object snaps to the ground is what varies the most from game to game... One of the most common solutions is to sample only a single point directly below the center of the object, and the other is to sample both sides and go with the highest. Both of these methods have advantages and disadvantages, and should be tweaked to be able to handle all kinds of structures you plan on drawing in your levels.

Quote:
I kind of wonder how collision maps are generally stored. I'd probably do a byte per block. This way, when you're dividing the object's position and width/height, you can store that number into x or y and not do anything else and you'll be able to offset the collision table exactly where you want to.

Personally, I'm against having a collision map separate from the level map, for reasons explained in this post, unless the game is going out of its way to hide the grid where the map is defined, like tepples pointed out.
Re: How Do You Code BG Collision?
by on (#164658)
I found this document helpful in describing the various sections of a map: http://higherorderfun.com/blog/2012/05/ ... atformers/

Espozo wrote:
Isn't it like you load the object's x position, divide it by the width of each block to find the starting block, and also divide the hitbox width by the width of each block to find how many tiles over you look for?

That's the method I first tried with Castle Platformer and it would occasionally cause the player to fall through a wall if the player's width is not a multiple of the tile width. After a few days of tinkering I found that the formula {number to tiles to check = ((entity->left & 0x000F) + entity->width - 1) / 16 + 1} worked for a 16px tile map.

Espozo wrote:
I kind of wonder how collision maps are generally stored.

In my research I found most games use a MetaTile format, with a simple index lookup to read the collision parameters of the selected metaTile.

For my engine, the map data is a grid of word indexes that point to the tile parameter tables (Structure of Arrays: tilemapdata, collision table, physics table, function table).

Currently my collision table is just 2 bits (solid and platform), I'll be adding more things like slopes to it later.
Re: How Do You Code BG Collision?
by on (#164661)
UnDisbeliever wrote:
That's the method I first tried with Castle Platformer and it would occasionally cause the player to fall through a wall if the player's width is not a multiple of the tile width

Oh yeah... If the collision isn't even at least the width of the tile, it won't count as anything... I guess it's like divide by the width of the tile, (ror) and if there's a remainder, (since the bits wrap around, check to see if the value is at least a certain side and then discard the bits) add one. This definitely seems a lot easier than the shenanigans I was trying to pull of with vram.
Re: How Do You Code BG Collision?
by on (#164662)
You can simply do:

Code:
BlockLeft = ObjectLeft / BlockWidth;
BlockRight = ObjectRight / BlockWidth;
BlockBottom = ObjectBottom / BlockHeight;

And then check all blocks between (BlockLeft, BlockBottom) and (BlockRight, BlockBottom). It might be only 1 block, if the hitbox is narrower than a block's width, but it will never be less than 1 block.
Re: How Do You Code BG Collision?
by on (#164676)
Shouldn't there be another one for checking against the ceiling?
Re: How Do You Code BG Collision?
by on (#164677)
Well, it's either the floor or the ceiling, because objects can only go up OR down, not both. The same goes for left and right. Unless the level geometry can change around the objects, or there's another layer with a moving map that objects can collide with.
Re: How Do You Code BG Collision?
by on (#164683)
I nice trick I found for handling slopes is to use "underslope" tiles. Each slope tile requires a certain type of tile underneath to "catch" a sprite from falling through a slope.
Re: How Do You Code BG Collision?
by on (#164686)
In my engine, slope blocks are identified as such, and if you want to know whether a certain point in them is solid, you have to look at their height maps, which are arrays containing the height of each column of a block.

Having objects stand on slopes is the easy part, really. The complex things about slopes is dealing with ledges, peaks, and concave constructions. At the same time you want to minimize the amount of points to sample when looking for collisions for performance reasons, you know you need to gather a lot of information about the terrain so you don't make bad decisions.

Another thing that complicates slopes, in my experience, are rotations that result from running up walls and ceilings. If old consoles could smoothly rotate sprites and hitboxes, this would be easy, but since they can't, you have to be a little creative to prevent rotations from looking jerky.
Re: How Do You Code BG Collision?
by on (#164688)
Getting "creative" may involve slightly shearing the sprite as a substitute for rotation.
Re: How Do You Code BG Collision?
by on (#164689)
tepples wrote:
Getting "creative" may involve slightly shearing the sprite as a substitute for rotation.

Indeed! Unfortunately, this technique doesn't work for all kinds of sprite arrangements... it's great for grids of 8x8 sprites, but not so much for unaligned 8x16 sprites, so this is not an option for me. Fortunately, you can still make the movement look smooth even if the sprite animation is jerky, by using the center of the object as an anchor, instead of the feet, and, as a consequence, skipping small sections of ground/wall/ceiling when the orientation changes.
Re: How Do You Code BG Collision?
by on (#164690)
It definitely won't work well if the minimum sprite size you're using is 16x16. However, since most SNES games were way bigger, they could store more predrawn angles for the objects. Really though, unless you're Bragalad, memory isn't even really a problem anymore. (Granted, it's not good to waste it, but I wouldn't purposely go out and limit myself because of it.) Is there any demo of someone doing real time sprite rotation on the SNES, or even the NES? It's probably frighteningly slow.
Re: How Do You Code BG Collision?
by on (#164697)
psychopathicteen has written one or two examples of realtime rotation for the SNES, but I don't remember where to find them.
Re: How Do You Code BG Collision?
by on (#164698)
And tepples has done it on the NES, IIRC. Or was it just scaling? Can't remember.
Re: How Do You Code BG Collision?
by on (#164705)
I think tepples only did scaling.
Re: How Do You Code BG Collision?
by on (#164709)
It was indeed just scaling, though this scaling can be combined with shearing.
Re: How Do You Code BG Collision?
by on (#164733)
My current code is 100% tile based which is trivial, but I plan to change it in a future project to use traditional bounding box based collision with the background.

It would work pretty much as discussed in this thread: you start at the destination X and Y position of the sprite. You then loop over the width and height of the sprite (from the starting position) checking tiles to see if they're solid as you go. If no solid tiles are found, then the sprite is moved, otherwise it isn't. Additional code is executed if the sprite collided to make sure that the sprite is moved to the edge of the tile that it collided with.

This is not very efficient, but it works for sprites of arbitrary sizes moving at arbitrary speeds in arbitrary directions, and it's very simple to implement.

The alternative is to only sample a couple of individual points based on known factors like the speed of the object, the size of the tiles, and the size of the sprite. For example, if Mario is moving left, you can do a single check a constant finite distance ahead of him to determine if he needs to stop. This is wildly more efficient than the above method, but it has limitations. You have to know about the exact sprite size involved, the velocity involved, and the tile sizes involved. So it'll be different for most different combinations, and it fails in certain circumstances.
Re: How Do You Code BG Collision?
by on (#164734)
If I were making a game, I'd make most of the enemy bullets one point and have a special routine for it, because you don't need to check height and width then. I already have a special metasprite code for dealing with one sprite objects, because it's not like you need to rearrange it whenever the object is being flipped vertically or horizontally. I someday want to share my game engine (I believe there aren't a lot of people making games for the SNES because of this stuff) and people with little programming knowledge could use it and work around it, and (hopefully) they'd start to learn more about programming as they go along (I mean, they'd still need some knowledge for coding objects and stuff) and would adjust it how they want it. Maybe this is an unrealistic idea, but I find making an entire game by myself to be much more unrealistic. A lot of decisions I've been making are to make this as flexible as possible, like the vram slot idea so people wouldn't have to worry about it.