Re: 8x16 and whatever else unreg wants to know
Posted: Tue Apr 03, 2018 6:11 pm
Can the data be compressed? If the data cannot be compressed because you need fine-grained random access, can the last tile of the bank remain unused?
NES Development Forums
https://forums.nesdev.org/
Your reset code (where you set the fixed $C000 mode) won't run if the cart boots in a bank without a reset stub. The reset stub is necessary precisely to guarantee that your reset code gets called.unregistered wrote:Before our game starts (is playable), it sets MMC1 into fixed-$C000 mode. So that would nullify the reset_stub's usefulness in each PRG bank, I think; but, that's just me thinking... am I right?
Code: Select all
; This goes at $FFF0 in all 16K banks
resetpart1:
sei
ldx #$FF
stx resetpart1+2 ; Clear shift register; set fixed $C000
txs ; Initialize stack pointer
jmp resetpart2
.addr nmi_handler, resetstub1, irq_handler
; This goes at the same place in both fixed banks, such as $FF80
resetpart2:
inx ; now X is 0
stx $2000 ; disable NMI handler
stx $2001 ; disable rendering
; Set CHR window to bank 0
stx $A000
stx $A000
stx $A000
stx $A000
stx $A000
; Set mode to $0E: vertical mirroring, fixed $C000, 8K CHR
lda #1
stx $8000 ; X writes a 0, A writes a 1
sta $8000
sta $8000
sta $8000
stx $8000
jmp resetpart3
; And this goes in your primary fixed bank
resetpart3:
cld
bit $2002
@vsync1:
bit $2002
bpl @vsync1
@vsync2:
bit $2002
bpl @vsync2
; Hardware init continues here, including choosing a PRG bank
; at $8000 by writing to $E000
1.) How many points does one need to check if the sprite is 8 pixels wide and she is colliding into an 8 pixel wall, but she moves more than 8 pixels per frame sometimes. I been attempting to solve this for a while. Now, two points are checked and she used to never enter the wall, but she is ejected 8 pixels away from the wall every other time it checks collision.Kasumi[color=#FF8080], [url=https://forums.nesdev.com/viewtopic.php?p=91421&sid=afaed212db3f60a1730db7676f794c05#p91421]on page 32[/url],[/color] wrote:I don't quite see the relation to collision detection.My goal is to see this collision detection work well.
But sure, I can sure talk about it. It's actually really easy and fast as long as you don't handle slopes, and don't have things like tiles you can jump up through or tiles you can drop down through.
You only really need to check 6 points to be TOTALLY safe, or 4 if the shape of your character never changes and he is smaller than the size of the collision tile for both X and Y. If he's say... 24 pixels tall with 16x16 collision tiles you have to check one more point. You also need to check more if your character moves more pixels in a frame than the size of your tile. For instance, he's capable of moving 9 pixels in a frame, but your collision tiles are only 8 wide.
Obviously, you first gotta find out whether or not the tile you're working with is collision enabled. I can't especially help with that unless you make a post about your current tile format. Post about that, and I can write a book for you about that.
I personally keep 4 screens of 32x32 metatiles in a page of RAM, which are updated when the game scrolls to a new screen. This means all my screen decompression happens once and only on a frame when the character passes a screen boundary.
The collision detection and parts of my code that tell the PPU what to draw always pull from this RAM, where it is much easier to figure out the math for me than pulling directly from the data in ROM.
I load the 32x32 tile from RAM, I find out the 16x16 tile I want in it. Then I know if I should eject or not.
It works exactly like this:The tricky part is actually getting the 32x32 tiles into RAM in the first place, but you may not even need to do that depending on how your data is stored.Code: Select all
;Ignore the <'s, it's a nesasm thing. lda <xhigh;Zero Page RAM I put any high X position into ror a;puts the lowest bit of the high x pos into the carry lda <yhigh;Zero Page RAM I put any high y position into ror a;puts the lowest bit of the high y pos into the carry ;and puts the x bit into the high bit of the accumulator ror a;Now the low x and y bits are in the high bits accumulator and #%11000000;Removes the other junk in the accumulator sta <reservedE;Temp RAM. The highest bits of it now contain ;which screen I'll read from RAM. Each screen is 64 bytes long, because it contains 8x8 32x32 metatiles. lda <xlow lsr a lsr a lsr a lsr a lsr a;divide by 32, since I'm trying to get a 32x32 metatile. ;The other bits of precision don't matter. ora <reservedE;This combines my "which screen" bits with the X offset in RAM. sta <reservedE lda <ylow lsr a lsr a and #%00111000;This takes away all unnecessary bits from ;my y position ora <reservedE;I know have the offset in RAM that I need to load my 32x32 tile from. tay;Y contains the address of the 32 x 32 metatile to look up lda $0500,y;loads 32 x 32 tay;Y contains the 32 x 32 tile number
I then use a series small series of branches to determine which part of the 32x32 tile I'm in. Topleft, Topright, bottomleft, or bottomright. Once I know that, I load the tile and use its format to determine whether it's a collision or not.
Here's how I eject when I know the current tile is collision enabled
You want to eject left while traveling right. So imagine the right point of your character has just entered the first pixel of a tile. It's anded position would be 0. In this case, you obviously want to eject one. If you're going super fast, you'll end up further in the tile, so the and will be higher and you'll always eject one more than that. Easy. jsr to the routine, subtract offset from your character's position. (It should be set to 0 if the collision routine detects the current tile is not a collision tile.) You can even use the offset variable the routine returns to find out whether or not there WAS a collision if you want to do that to stop a walking animation or something.Code: Select all
;Ejecting up/left lda lowbyte and #%00001111 clc adc #$01 sta offset rts
This one is mostly the same, except it has an eor. Say you're traveling left and hit the first pixel of a tile. That's actually pixel $0F, but you only want to eject one! The eor makes $0F equal to one, then it works the same as before! As before, just write a 0 to offset if you've discovered the current tile is not solid. Jsr to routine, then add offset to player's position.Code: Select all
;Ejecting down/right lda lowbyte and #%00001111 eor #%00001111 clc adc #$01 sta offset rts
This works for any size tile that's a power of two. Just change the ands and eor to the number of bits your tile takes up.
If you're traveling left, move the player using the current speed value. You may be in a wall. You check the top and bottom left points of your character, and eject right. (add offset) If you're traveling right, check the top and bottom right points of your character and eject left. (subtract offset) you'll never be in the wall for a frame like Mario, and this will certainly be fast enough for how few tiles you'll need to check.
For ejecting up while falling you check the bottom left and right points and subtract offset from the player's position.
Code: Select all
lda altoX ;<our major X variable
and #00000111b ;and with 7 because our collision deals with 8x8 tiles :) ;always clears bit3
ldx FORWARD ;<00 for left, 01 for right, and ff for neither
;it is verified that FORWARD won't be ff if this code is reached :)
bne + ;if going right we don't want to invert bits 0 through 2
eor #00000111b
+ ;some other code here
sta t16 ;a temp variable
lda altoX
ldx FORWARD
beq +
clc
sbc t16
jmp ++
+ sec
adc t16
++
sta altoX


Code: Select all
FCEUX 2.2.3 - Trace Log File
f1 A:00 X:FF Y:00 S:C2 P:nvubdIzc $BFF0:78 SEI
f1 A:00 X:FF Y:00 S:C2 P:nvubdIzc $BFF1:A2 FF LDX #$FF
f1 A:00 X:FF Y:00 S:C2 P:nvubdIzc $BFF3:9A TXS
f1 A:00 X:FF Y:00 S:FF P:NvubdIzc $BFF4:8E 00 80 STX $8000 = $00
f1 A:00 X:FF Y:00 S:FF P:NvubdIzc $BFF7:4C A0 FF JMP $FFA0
f1 A:00 X:FF Y:00 S:FF P:NvubdIzc $FFA0:00 BRK
f1 A:00 X:FF Y:00 S:FC P:NvubdIzc $D1BA:00 BRK
etc.