Page 1 of 3
Collision data: 8 or 16 bit?
Posted: Fri Jan 15, 2016 9:29 am
by zkip
I'm working on turning sprite to tile coorinates and I'm having trouble with the maths involved. What exactly are my options here? At first for a simple test I'm first of all allowing the movement of the player. Then checking the direction and branching to either an horizontal collision check or vertical depending on direction. From there I turn sprite coords into tile by the (Y/8)*32+X/8 via lsr asl combo. It sort of works until I realized that for that I need it to be 16 bit as the uncompressed collision map is. What are my options here? Do I need to turn the Collison map into bit values where 1 bit = 1 tile? But then I would still need the math to have that 16 bit number, right?
-Thanks, zkip.
Re: Collision data: 8 or 16 bit?
Posted: Fri Jan 15, 2016 9:40 am
by GradualGames
Can you provide a bit more context about how you've structured your map data? Is it designed for single screen, scrolling, multi directional scrolling? *edit* What are the dimensions of a map? How big are your tiles, and do you use metatiles?
Re: Collision data: 8 or 16 bit?
Posted: Fri Jan 15, 2016 9:47 am
by zkip
Single screen. Simplest you can get. I do plan on later doing more but I'd assume for relearning purposes the simplest would be most understandable without worrying about scrolling, tile types,metatiles, etc getting in the way.
The nametable data is arranged in such a way that it can also be used as the Collison map.
Re: Collision data: 8 or 16 bit?
Posted: Fri Jan 15, 2016 10:02 am
by GradualGames
That's a good approach to start with single screen first. You could structure your collision data any way you like. Since you're just starting out, I'd recommend keeping it simple. I'd probably keep an array in ram of 16 * 15 bytes, where each byte contains information about a single, attribute sized tile (16x16 pixels). This byte could be a bit field where one bit is used for "solid," another for "hurt," and perhaps other information relevant to your game. To index such a map, you might do something like:
Code: Select all
MAP_SOLID = %00000001 ;a mask for the solid bit. you can add other masks for other information
;this could be in zp used as parameters for a routine to look up map data
map_x: .rs 1
map_y: .rs 1
;this could be in zp, these are just temporary values for the routine to use.
row: .rs 1
column: .rs 1
;this would be in RAM
map_data: .rs 16 * 15
;this would be in a code bank
;Here's a routine for getting the byte from the map based on 8 bit X and Y (single screen coordinates, 16 bit not needed)
;It expects map_x and map_y to contain a coordinate in screen space (pixels)
;It will return with the accumulator containing the byte describing the tile that map_x and map_y intersects.
get_map_byte:
;divide map y by 16 to get your row
lda map_y
lsr
lsr
lsr
lsr
sta row
;divide map x by 16 to get your column
lda map_x
lsr
lsr
lsr
lsr
sta column
;now we compute location in map_data by row * 16
lda row
asl
asl
asl
asl
;now add the column on
clc
adc column
;use computed index to look up byte
tax
lda map_data,x
rts
;Now you can use this routine (elsewhere in your code) to get information about a tile on the screen.
;load some arbitrary coordinates into the map_x and map_y params. You'd probably use a collision point under your character's feet or somewhere else around them depending on the direction they are going.
lda #100
sta map_x
lda #120
sta map_y
jsr get_map_byte
and #MAP_SOLID
beq not_solid
;do something if tile is solid
not_solid:
Hope that helps!
*edit* You could put your map data in ROM, too. Since it's in RAM in this example, you could have it change dynamically during gameplay, but you'd need to load useful data into it, first.
*edit* Forgot to demonstrate loading x and y coordinates into the parameters. Fixed.
Re: Collision data: 8 or 16 bit?
Posted: Fri Jan 15, 2016 10:24 am
by zkip
Thanks for the detailed reply! Although, it looks like that is for a screen with 16x16 metatiles? I'm assuming in the case of collisions this method is easier? My current setup of the level is all 8x8 tiles. Which is why I was asking about the 16 bit. Away from computer right now, but from memory a 32x30 8x8 tile screen is far above anything accessible from 8bit registers.
Re: Collision data: 8 or 16 bit?
Posted: Fri Jan 15, 2016 10:30 am
by GradualGames
Yeah---using tiles that are 16x16 in size, you can get away with only 8 bit indexing as in the example, so it is very simple. For indexing for 8x8 tiles for single screen, you'd have 32x30 (960 total) tiles, you can change the right and left shifts to only shift by 3 instead of 4 (divide or multiply by 8 instead of 16). Then when you compute the location in your map data, if you still use one byte per tile, you will have to extend into 16 bits (because if you have 32x30 tiles, one byte each, it'd be 960 bytes). If I get another sec soon I'll try to rework the example extending to 16 bit indexing.
Re: Collision data: 8 or 16 bit?
Posted: Fri Jan 15, 2016 12:29 pm
by GradualGames
Here's the example modified for the case you were interested in. 8x8 tiles, single screen map. Note this is just one way of doing it, probably the simplest to follow. You could of course come up with a collision map that really is one bit per tile rather than one byte per tile, then you'd need to do some more bit rotation and shuffling things around to get at the bit you want. But this approach is good for being able to easily expand to using multiple bits for each tile, for additional information like hurt, item, etc. etc.
Code: Select all
MAP_SOLID = %00000001 ;a mask for the solid bit. you can add other masks for other information
;this could be in zp used as parameters for a routine to look up map data
map_x: .rs 1
map_y: .rs 1
;this could be in zp, these are just temporary values for the routine to use.
row: .rs 1
column: .rs 1
;fully computed 16 bit address of tile within map. this will need to be zp so you can use indirect addressing.
map_tile_address: .rs 2
;this would be in RAM (*edit* or ROM. Probably simpler to try it out in ROM first, make a 32x30 bunch of 0's and 1's with a define byte directive to define your solid/not solid map)
map_data: .rs 32 * 30
;this would be in a code bank
;Here's a routine for getting the byte from the map based on 8 bit X and Y (single screen coordinates, 16 bit not needed)
;It expects map_x and map_y to contain a coordinate in screen space (pixels)
;It will return with the accumulator containing the byte describing the tile that map_x and map_y intersects.
get_map_byte:
;divide map y by 8 to get your row
lda map_y
lsr
lsr
lsr
sta row
;divide map x by 8 to get your column
lda map_x
lsr
lsr
lsr
sta column
;now we compute location in map_data by row * 32
;first we load row into the lo byte of map_tile_address
lda row
sta map_tile_address
lda #0
;then we set the hi byte of map_tile_address to 0. Now we do a 16 bit shift (5 times, to do * 32) to compute the correct address.
sta map_tile_address+1
;now we do a 16 bit shift left to multiply by 32.
lda map_tile_address+1
asl map_tile_address
rol
asl map_tile_address
rol
asl map_tile_address
rol
asl map_tile_address
rol
asl map_tile_address
rol
sta map_tile_address+1
;now add the column on, sign extending the 8 bit column which we know is positive
clc
lda map_tile_address
adc column
sta map_tile_address
lda map_tile_address+1
adc #0
sta map_tile_address+1
;finally add on the base address of our map data
clc
lda map_tile_address
adc #<map_data ;(use #low(map_data) if using nesasm...)
sta map_tile_address
lda map_tile_address+1
adc #>map_data ;(use #high(map_data) if using nesasm...)
sta map_tile_address+1
;use computed index to look up byte
ldy #0
lda (map_tile_address),y ;note, nesasm uses [ ] instead of ()
rts
;Now you can use this routine (elsewhere in your code) to get information about a tile on the screen.
;load some arbitrary coordinates into the map_x and map_y params. You'd probably use a collision point under your character's feet or somewhere else around them depending on the direction they are going.
lda #100
sta map_x
lda #120
sta map_y
jsr get_map_byte
and #MAP_SOLID
beq not_solid
;do something if tile is solid
not_solid:
Re: Collision data: 8 or 16 bit?
Posted: Fri Jan 15, 2016 1:07 pm
by zkip
Thank you, you have been more than helpful to me. I have one more question and I think I'll have this. In the order of things you'd first let the player move. Then check for a collision and if true I've heard to "eject" the player. What exactly is this eject? Move the player a predetermined amount or does it mean move 1 pixel until the collision no longer exists?
Re: Collision data: 8 or 16 bit?
Posted: Fri Jan 15, 2016 1:14 pm
by GradualGames
An eject is essentially to figure out how many pixels to pop your character out of a tile. I usually use AND for this. I usually AND the coordinate with %00001111 to see how far a character is poking into the (16 pixel wide) tile, then I subtract this value from their coordinate (if you're moving in the positive direction on the x or y axis). Depending on the speed of your character, this may require additional logic to cover additional edge cases, but that's the most basic step for ejection.
*edit* I didn't cover the case for ejecting from moving the other direction. If I get another spare moment I'll try to illustrate that as well, though maybe tokumaru's linked post already covers this.
Re: Collision data: 8 or 16 bit?
Posted: Fri Jan 15, 2016 1:14 pm
by tokumaru
zkip wrote:Move the player a predetermined amount or does it mean move 1 pixel until the collision no longer exists?
Neither. A predetermined amount doesn't work if objects move at variable speeds, and testing for collisions repeatedly after moving 1 pixel would use too much CPU time. You should move it the number of pixels that went into the solid block.
This post might help.
Re: Collision data: 8 or 16 bit?
Posted: Fri Jan 15, 2016 4:24 pm
by darryl.revok
Quick Tip:
Move your character horizontally, and then check for collisions on the X axis.
Then move your character vertically, and then check for collisions on the Y axis.
If you try to move both at once, you'll never be able to properly determine if you landed on the top or side of a corner.
Re: Collision data: 8 or 16 bit?
Posted: Fri Jan 15, 2016 6:28 pm
by zkip
I see now. Thanks everyone. Also, thanks for that info darryl. I've always heard to do that but never known exactly why.
Re: Collision data: 8 or 16 bit?
Posted: Fri Jan 15, 2016 11:01 pm
by dougeff
Here's a thought...
32 tiles accross, 5 bits from X and Y. Add to address of data = tile.
Code: Select all
Lda #0
Sta Temp_High
Lda Y_position
And #$f8
Asl a
Rol Temp_High
Asl a
Rol Temp_High
Sta Temp_Low
Lda X_position
And #$f8
Lsr a
Lsr a
Lsr a
Clc
Adc Temp_Low
Sta Temp_Low
Ldx Which_Room
Lda Table_of_Map_Addresses_Low, x
Clc
Adc Temp_Low
Sta Pointer
Lda Table_of_Map_Addresses_High, x
Adc Temp_High
Sta Pointer+1
Ldy #0
Lda (Pointer), y
;equals the tile
Tax
Lda Collision_Array, x
Beq No_Collision
Jsr Eject
No_Collision:
This allows for multiple rooms, each with their own map.
Re: Collision data: 8 or 16 bit?
Posted: Sat Jan 16, 2016 9:33 am
by zkip
GradualGames wrote:Here's the example modified for the case you were interested in. 8x8 tiles, single screen map. Note this is just one way of doing it, probably the simplest to follow. You could of course come up with a collision map that really is one bit per tile rather than one byte per tile, then you'd need to do some more bit rotation and shuffling things around to get at the bit you want. But this approach is good for being able to easily expand to using multiple bits for each tile, for additional information like hurt, item, etc. etc.
I just now got around to messing around with the code you provided and I have one more question. This returns the 16bit position, but how exactly do I index that with the data I have in ROM? I've tried clc and adcing the high and low bytes of the address where the collision data lies, however this works for certain parts of the screen an the other parts are returning with arbitrary numbers such as 1f. Further more, this doesn't seem right to me. Surely there's another way to index it?
Re: Collision data: 8 or 16 bit?
Posted: Sat Jan 16, 2016 10:03 am
by GradualGames
The 16 bit address should point directly to the byte you want that represents data for an 8x8(pixel) tile on a 32x30(tile) sized screen. Just don't change y and read that byte (as in the routine) and you can find out the data for that tile. Any time you want information about another tile on the screen just call get_map_byte again with a different X and Y coordinate. Since we have to look up something that goes beyond 256 bytes you have to use indirect addressing---the index register will typically be zero in this case. If there's a significantly better way of doing this, I'm eager to learn it, myself.
*edit* Perhaps you are asking about the array containing your map data. In the example, I suggested placing it in RAM. In this case you would need to load this array with data that makes sense, and you would gain the advantage of being able to change the data at runtime, easily. However, you can put this data in ROM, instead. Note I made an error (which I fixed yesterday) in the 16 bit example: I should have allocated 32 *30 bytes for map_data, not 16*15, which was for the earlier example.
The easiest thing would probably be to design a 32x30 map of bytes in ROM with data about your map (0's for not solid 1's for solid), and then read that.