Object collision

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

User avatar
Bregalad
Posts: 8056
Joined: Fri Nov 12, 2004 2:49 pm
Location: Divonne-les-bains, France

Post by Bregalad »

Design the data as you like, I recoomend not using any compression the first time, then, you can found some compression algorithms on the net or make your own, depending of how the data is organized.
The usual way is to beak a map into blocks, and each blocks will have it's own index to tile data, attributes, collision and stuff. You can do as much "block data levels" as you like. You can also do blocks that will point to other smaller blocks (I do this in my game).
To set it on the screen, turn it off then write all NamTables, and palettes to eventually turn it on later.
Useless, lumbering half-wits don't scare us.
-tokumaru-

Post by -tokumaru- »

Bregalad wrote: To set it on the screen, turn it off then write all NamTables, and palettes to eventually turn it on later.
Just a little question here: do you *NEED* to turn the screen off to write to the PPU during vblank? Or is it just a safety thing? I'd bet it is just to be safe...
-tokumaru-

Post by -tokumaru- »

Bregalad is right Celius. You shouldn't be worried about RLE or any compression just yet. And since you're working on a pacman kind of game, the maps are not very large and don't occupy much space, so you don't need compression anyway.

I think you should draw your maps directly in ROM, using .db statements. Arrange your map in a 2-D array (32x30, for example). So after your player just moved, and you want to check for a collision against a specific block (at position XPos, YPos), just use this formula to read the block from ROM:

(base map address) + (YPos * 32) + XPos

This will give you the right address of the block you want to ckeck. Once you loaded it, you can tell if it is solid or empty, and decide if the player can go through or not.
Celius
Posts: 2158
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

Okay, but is there a way to say that if your player's x and y pos reach this tile number (I'm referring to in the pattern table, not name table), collide? I'm kind of getting an idea how to do this, but not really. Please explain if there's any way to do that.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

Celius wrote:Okay, but is there a way to say that if your player's x and y pos reach this tile number (I'm referring to in the pattern table, not name table), collide? I'm kind of getting an idea how to do this, but not really. Please explain if there's any way to do that.
Well, you might just make up a "code" like this: tiles from 0 to 127 are "empty" and tiles from 128 to 255 are "solid". If your game does not use many graphics, this works well.

Just load the tile number from your map, as I said before. If it is in the range 128->255 (bit 7 set) you don't let the player go by. There is your collision.

Just be carefull about one thing: your player coordinates have pixel-accuracy, so you must get rid of that when checking the background, wich has only tile-accuracy. So, you must divide the player coordinates by 8 (shift right 3 times) to get the tile coordinates. Also, you must account for your player's size when checking for collisions to the right and bottom. But of course, this can vary depending on the way you handle your player's coordinates (I'm assuming the player coords refer to the top left corner of it).
Celius
Posts: 2158
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

Well, I'm just trying to figure out a way to make a semi-small routine that will automaticly check for collision. Is there any way that you can have the routine automaticly check every byte to see if bit 7 is set, for every map? not like, oh yeah, just check each individual tile on the Name Table for collision, I don't want to do that. I wish you could just say this:

collide:
load 1 byte, starting with the first one
if bit 7 is set
collide
load 1 byte, starting with the first one
if bit 7 is not set
don't collide
go to next byte
loop this routine for a long long long time.

Do you know what I mean? instead of repetative asm that is tedious and ends up causing problems, just have one nice little routine. Is this possible?
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

Celius wrote:Well, I'm just trying to figure out a way to make a semi-small routine that will automaticly check for collision. Is there any way that you can have the routine automaticly check every byte to see if bit 7 is set, for every map?
Why would you want to check the whole map? You only need to check the tiles close to the player, since those are the only tiles the player can collide with. If the player is at the bottom of the screen, for example, there is no way he can collide with a block at the top of the screen!

Look at the pseudo-code example I posted a while ago. You move the player, still not knowing if this movement is allowed or not. Then you check if the player "entered" a wall. Now, if your player was moving right, you only need to check the tiles to his right! If you are moving to one direction, there is no way you'll collide in the other! See the beauty of it?!

In the example I posted, I only checked for the tiles where the player was. That's what you have to do. Only check the surroundings of the player. If you moved right, check the tiles to the right. If they are empty (no bit 7), let it be. Otherwise, move the player back. This will cause the effect of collision.

Look, I can't give you'exact information since I don't know the size of your player or the size of your tiles, etc. But tipically you'll only need to check a few tiles in the direction you are moving. Depending on the size of the player, something around 3 checks is enough. If you check the whole map, It will be terribly slow, and there is absolutelly no reason for this.
Celius
Posts: 2158
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

Oh, so something like this?:

Code: Select all

rightkey:
            jsr check_collision
            lda rtcol
            cmp #1
            beq collide
            dec sprx
            rts

collide:
          rts

check_collision:
            load tile (x+1), the tile next to the one you're over
            see if bit 7 is on
            bne no_col
            lda #1
            sta rtcol
            rts
 
no_col:
           rts
Okay, I think that would be okay, I'd just need a different way to say load tile (x+1), and see if bit seven is on. By the way, this is a very newbie question, but how do you see if certain bits are on? not just comparing like this:

cmp #$80

I mean like

cmp #%10000000

but without the next 7 bits after number 7. I know that's probably confusing, but you know what I mean. I just want to see if bit seven is on. Not if the first four bits are exactly as I write them, JUST bit 7. Any way to do that? and how would I do the tile (x+1) thing? Sorry...
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

I'll check the rest of your post in a while I'm a bit busy right now!

But the bit thing, I suggested bit 7 because it is the easiest to check for. Just use BMI or BPL, wich will branch on bit 7 set/clear.

But for the other bits, there are many ways to test them. You could for instance AND the number with your mask, and if the result is not zero, the bit is set. I believe BIT does the same thing, but doesn't destroy the accumulator.
Celius
Posts: 2158
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

Okay, so how exactly would you say I see if bit 7 is on? only bit 7? I don't care about the other bits, I only care about bit 7. Is there any way I could say something like this?:

cmp #%1 ;don't care if any other bits are on, just bit 7

If I tried that, It would just say like:

cmp #$01

I can't really explain why, but I know why, It's just hard to explain. So yeah, what ways could that be said? It would really help... :(
User avatar
Bregalad
Posts: 8056
Joined: Fri Nov 12, 2004 2:49 pm
Location: Divonne-les-bains, France

Post by Bregalad »

You mean that the whole 8-bit argument is a waste to check only one bit, right ? If so, yeah, some processors have the ability to check a bit like this. The SPC-700 and CMOS 6502 can, I think. But the NMOS 6502 isn't able to do this. Instead, just load the tile value in the accumulator, and check for the minus flag. If a value above $80-$ff, it will be set (bmi will branch) else, it will be clear (bpl will branch).
It's just like the vait for VBlank at reset is typically done :

Code: Select all

lda $2002
bpl -
lda $2002
bpl -      ;Check twice for bit 7 of $2002
In that case the code would look like :

Code: Select all

lda #<MapAdress
sta PointerL
lda #>MapAdress
sta PointerH
lda PlayerHPos
lsr A
lsr A
lsr A
sta Temp       ;Get HPos/8 in temp
lda PlayerVPos
and #$f8       ;The value is already multiplied by 8
asl A            ;By 16 now
rol Temp2     ;Save MSB
asl A           ;Final result multiplied by 32
rol Temp2
adc Temp
adc PointerL
sta PointerL
lda Temp2    ;Final addition to get the pointer ready
adc PointerH
sta PointerH
ldy #$00
lda [Pointer],Y
bpl _noCollision
inc ColisionFlag
_noCollision:
rts
Useless, lumbering half-wits don't scare us.
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

"Bit 7" usually means the most significant bit, the one with place value $80 = 128. The bit with place value $01 = 1 is called "bit 0".

If you want to test whether bit 0 is on, use 'and #$01', or 'lda #$01 bit address'.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

You don't need any CMP or anything. If you read ANY 6502 document, you'll see that the load instructions (LDA, LDX, LDY) affect the N (negative flag). The negative bit is bit 7 (if this is set, the number can be interpreted as negative if you want). When you load a value, whatever is in bit 7 is copied to the N flag. then you just use BMI or BPL wich branch depending on the contents of the N flag. Like this:

Code: Select all

	LDA SOMEWHERE	;Loading the map tile.
	BPL BIT7CLEAR	;Skip collision if bit 7 clear.

	;If you're still here, there is a collision.
	;So you should move the player back.

BIT7CLEAR:
	;If you get here there is no collision.
	JMP MAINLOOP	;Just loop back.
Of course the LDA part won't be direct like this. When reading your map, you'll most likely use (Indirect),Y addressing. So, if your map is 32x30, like I said before, you can do it like this:

Code: Select all

	;Load player Y coordinate.
	LDA PlayY

	;Divide by 8.
	LSR
	LSR
	LSR

	;Now, I have to explain what I'll do here.
	;We must multiply the value in the accumulator
	;in order to get the offset of the row the player
	;is at. And we have to add that value to the base
	;address of the map (address of the start of the map).
	;But in this case, it is more efficient to add before
	;multiplying, so I divide the base address of the map
	;(in the example, $C000) by 32 (wich results in $600),
	;so it will restore it's value when I multiply
	;everything by 32.
	;In this case we don't even need to add, since the
	;lower byte of the address is 0.
	STA MAP_ADD+0
	LDA #$06
	STA MAP_ADD+1

	;Now, multiply by 32.
	ASL MAP_ADD+0	;x 2
	ROL MAP_ADD+1
	ASL MAP_ADD+0	;x 4
	ROL MAP_ADD+1
	ASL MAP_ADD+0	;x 8
	ROL MAP_ADD+1
	ASL MAP_ADD+0	;x 16
	ROL MAP_ADD+1
	ASL MAP_ADD+0	;x 32
	ROL MAP_ADD+1

	;Now, load PlayX.
	LDA PlayX

	;Divide by 8.
	LSR
	LSR
	LSR

	;Put it into Y.
	TAY

	;Finally, load the byte from the map.
	LDA (MAP_ADD), Y
Wow, that took a while. I hope you understand. This is the simplest way I can think of reading a byte from a map.

Your map must be define like this:

Code: Select all

	.org $C000
	.db $00, $00, $00, $00, $86, $86, ... (complete the 32 bytes of the row)
	.db $A0, $00, $00, $00, $86, $86, ... (complete the 32 bytes of the row)
	.db $A0, $00, $00, $00, $86, $86, ... (complete the 32 bytes of the row)
	.db $B0, $00, $00, $00, $86, $86, ... (complete the 32 bytes of the row)
	.db $B0, $00, $00, $00, $86, $86, ... (complete the 32 bytes of the row)
	.
	.
	.
	(keep until you complete the 30 rows)
Now, Celius, I'm almost giving it all ready to to you, all you have to do from here is to account for the direction the player is moving. I mean, check the tiles to the right if the player is moving right, the one at the top if he is moving up. You just have to manipulate the making of the address I discussed above accordingly. Add 1 or 2 (of course it depends on your player's size) to the coordinates we are checking.

Good luck!
Celius
Posts: 2158
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

Oh my... I have no idea why I didn't think of that. I must find out why I didn't think of that, because my wonderful Science teacher gave learning advice: When you wonder why you didn't think of something, figure out why, and you'll never have that problem again. Okay, anyways, I think this is what you mean by checking bit 7:

Code: Select all

lda tilenumber
cmp #$80
bpl collide
rts
Maybe not exactly like that, but is that the general idea of what you were saying? Okay, well I could encorperate that in my code somehow. Thank you for telling me that.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

Celius wrote:

Code: Select all

lda tilenumber
cmp #$80
bpl collide
rts
No, no! you don't need the "CMP #$80" part! Just load the number! The LDA command affects the negative flag directly! don't compare anything, just load the byte!

Code: Select all

lda tilenumber
bpl dontcollide ;You don't want to collide on bit 7 clear, remember?
;now collide
This will do the trick.

Celius, do you understand how the instructions on the 6502 affect the flags? You don't always have to do a "CMP" to make a decision. The other instructions affect the flags too. The 6502 knows a number is negative (bit 7 set) just by loading the value. No comparision is needed.

Why don't you play with a 6502 simulator, to test this little things? Just test and you'll see what I'm telling you is the truth. Just load, don't compare. In this case, of course.
Post Reply