Scrolling RPG maps
Moderator: Moderators
Re: Scrolling RPG maps
I was thinking of only loading map data into RAM during map transitions, which I'd disable NMI interruption. So like, the player would leave a map (or start a new game), I'd disable NMI (and the screen would be all black during transition), load MAP data into RAM, store it in the PPU and re-enable NMI and the map would be rendered again.
But to be honest, I'm not a fan of having to store RAM memory for this, I don't like this idea.
But to be honest, I'm not a fan of having to store RAM memory for this, I don't like this idea.
Re: Scrolling RPG maps
I know what you mean, even with 16x16 metatiles you might still only have enough RAM for roughly two screens of the map. NPCs outside of screen might not be able to have collision logic when they are too far away from the player's party. If you make an RPG you probably want 8 kB SRAM in the cartridge which is required for saving anyway (unless you save in FLASHROM). On the other hand DQ1 and DQ2 used passwords with no external RAM at all.
Last edited by Pokun on Sun Apr 04, 2021 12:25 pm, edited 1 time in total.
Re: Scrolling RPG maps
Outside of the PC-Engine, this is how it works on the NES/SMS/MD/SNES and other similar systems; store updates in ram, then transfer from ram to vram during vblank.
I think 32x32 metatile format made up of four 16x16 tiles, is a better map format than RLE. It's a constant compression ratio, where as RLE is not. And metatile doesn't require as much ram as decompressing RLE. I guess you could throw away everything but the row or column you need from the RLE decompression, but it's still costs more processing time than metatiles.
The downside of RLE is that you need runs of the same value. This can dictate art direction if you need to hit a specific ratio for compression. The other downside of RLE tends to need additional vector assistance (pointing to segmented areas to decompress, rather than having the whole area one large compressed item).
The downside of metatiles is that all the combinations of items (tiles) used in your map could be more than the range you alloted. As in, you scan your map and it finds a total of 280 combinations of 16x16 tiles, to make 280 32x32 metatiles. But you want to use a byte (256) to store the 32x32 metatile value. So you need to reduce/alter art, or you need to expand the range. But you don't need to jump to "word" size though, which bloats it to double the size. You can have a separate 1bit version of the same map that holds the "Most Significant Bit" for a 9bit value (512 metatiles). Decoding is fast, and accessing row or columns is pretty fast too compared to RLE. You don't need to decode the whole segment to get your needed values (like you would with RLE).
Re: Scrolling RPG maps
Why would PC-Engine be any different?
Re: Scrolling RPG maps
Because it has much more freedom in the size of tile maps? Tile maps for the PCE can be (almost) any size and (almost) anywhere in the VRAM.
I don't know about SFC and its complicated graphics mode, but the FC only has enough memory for 2 screens, and the SMS only has 1 single screen. I've also read that the MD's tile map is just a bit larger than 1 screen, so any game that needs some serious scrolling would need more updates to the tile map.
I don't know about SFC and its complicated graphics mode, but the FC only has enough memory for 2 screens, and the SMS only has 1 single screen. I've also read that the MD's tile map is just a bit larger than 1 screen, so any game that needs some serious scrolling would need more updates to the tile map.
Re: Scrolling RPG maps
The SFC also has quite flexible VRAM, but yeah you can still only have up to 4 screens of BG data at a time, and since VRAM-to-VRAM-DMA isn't possible like it is on PC-Engine, you can't have an extra screen buffer in VRAM (unless I'm forgetting something).
Re: Scrolling RPG maps
MD scrollable tilemaps are 32/64/128 tiles wide and the same options for height, but also are limited to max 4x32x32 tiles. Games that want to use 320px mode have to pick a tilemap width that's 64 or 128 tiles. Almost all games seem to use the 64x32 tile (512x256px) arrangement.
Both tilemaps have to use the same dimensions, unlike the SNES. The fix tilemap ("window") automatically sizes itself to the resolution.
Both tilemaps have to use the same dimensions, unlike the SNES. The fix tilemap ("window") automatically sizes itself to the resolution.
Re: Scrolling RPG maps
After a lot of sweat and tears, I made very little progress in making the map scroll when the player moves to the right. I'm having a problem where the game freezes after the routine that draws a new tile column to the right of the screen is executed.
The code responsible for it seems to be this part, which is run at the beginning of the routine. If I comment it out, the game runs properly (but some glitches happen, probably because of the rest of the drawing routine):
Any ideas what I could be doing wrong this time?
The code responsible for it seems to be this part, which is run at the beginning of the routine. If I comment it out, the game runs properly (but some glitches happen, probably because of the rest of the drawing routine):
Code: Select all
LDA #%00000100 ; set to increment +32 mode, don't care about other bits
STA PPUCTRL
Re: Scrolling RPG maps
Are you sure you don't care about the other bits?
Especially, say, the NMI bit?
Especially, say, the NMI bit?
Re: Scrolling RPG maps
You can use ORA with #%00000100 to care about the other bits.
Re: Scrolling RPG maps
But don't ORA directly with $2000, since this PPU register is write-only, like I said before. What you can do is keep a copy of $2000 in RAM, then you can take this copy, ORA with %00000100, and write to $2000.
Re: Scrolling RPG maps
Oh right I forgot about that. VRAM access time is longer on PC-Engine so there is less need to buffer it in RAM I guess.
Back to topic, yeah something like this:
The ppuctrl_buffer RAM register must always contain what you want PPUCTRL to contain so when you enable rendering you write the same value to both the real PPUCTRL and to the buffer.
Back to topic, yeah something like this:
Code: Select all
lda ppuctrl_buffer
ora #%00000100 ; set to increment +32 mode, do care about other bits
sta ppuctrl_buffer
sta PPUCTRL
Re: Scrolling RPG maps
Ah yeah, keeping a copy of the previous ppuctrl bits and using it solved the freezing problem. I guess I misunderstood the example from the Nerdy Nights tutorial
Now I'm having the problem show in this video:
https://drive.google.com/file/d/1b9sO3Z ... sp=sharing
There are 2 things that seem wrong.
1) the map "shaking" while moving (this effect is barely visible on the video unfortunately)
2) the new column on the right is only half drawn. All the rest is black. I checked with the debugger and it seems like the routine is storing the correct bytes on PPU_DATA up until the end of the column.
This is my column drawing routine. For now, it's only supposed to draw one 8-pixel wide column (even though the player walks 16 bits). It also has to decompress the tiles. The RLE compressing method is the one described by Dwedit in this post http://forums.nesdev.com/viewtopic.php? ... 15#p269655 (but this routine doesn't deal with "uncommon" tiles yet, just the common ones)
Now I'm having the problem show in this video:
https://drive.google.com/file/d/1b9sO3Z ... sp=sharing
There are 2 things that seem wrong.
1) the map "shaking" while moving (this effect is barely visible on the video unfortunately)
2) the new column on the right is only half drawn. All the rest is black. I checked with the debugger and it seems like the routine is storing the correct bytes on PPU_DATA up until the end of the column.
This is my column drawing routine. For now, it's only supposed to draw one 8-pixel wide column (even though the player walks 16 bits). It also has to decompress the tiles. The RLE compressing method is the one described by Dwedit in this post http://forums.nesdev.com/viewtopic.php? ... 15#p269655 (but this routine doesn't deal with "uncommon" tiles yet, just the common ones)
Code: Select all
.proc UpdateNTScrollLR
;bit PPUSTATUS
LDA #%00000100 ; draw vertically (32 increment)
ora ppuctrl_mirror
STA PPUCTRL
ldy cameratiley
sty aux1
LDA camerax
LSR A
LSR A
LSR A
STA arg1_16 ; $00 to $1F, screen is 32 tiles wide
; set address in the PPU well write to
LDA nametable
EOR #$01 ; invert low bit, A = $00 or $01
ASL A ; shift up, A = $00 or $02
ASL A ; $00 or $04
CLC
ADC #$20 ; add high byte of nametable base address ($2000)
STA $2006 ; write the high byte of column address
LDA arg1_16
STA $2006
lda camerax ; where in the map is the scroll (ranges from 0 to mapwidth)
sta arg1_16
lda camerax+1
sta arg1_16+1
jsr lsr_16_bit
jsr lsr_16_bit
jsr lsr_16_bit
jsr lsr_16_bit
lda arg1_16
sta aux5 ; store which 16 pixel column needs to be drawn
@RLE_Rows_Loop:
ldy aux1 ; gets the pointer to the row we'll read the bytes from
lda (rowpointers), Y
sta aux1_16
iny
lda (rowpointers), Y
iny
sty aux1
sta aux1_16+1
ldy #$00
@RLE_Loop_Bytes_in_Row:
lda #0
sta aux2 ; stores the counter for how many metatile we've decompressed and read
lda (aux1_16), Y ; get metatile
sta aux3 ; get compressed mtt and store it
and #%00000111 ; we check if the last 3 bits are 111
cmp #%00000111
bne @RLE_Loop_Load_Common
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Common tile processing
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@RLE_Loop_Load_Common:
lda aux3
and #%00000111 ; get common mtt index
asl
asl ; each mtt has 4 bytes, so we multiply the index by 4
tax ; we save it in X
lda aux3
and #%11111000 ; we get how many tiles will be drawn
lsr
lsr
lsr
sta aux4 ; store how many tiles are compressed in this byte (this is the repeating mtt counter)
@RLE_Loop_Draw_Common:
dec aux4 ; decrement repeating mtt counter
lda aux2 ; get how many mtts we've read in this row
inc aux2 ; increment it for the next loop iteration
cmp aux5 ; have we reached the metatile we wanted to write into PPU?
bcc @RLE_Loop_Skip_Draw_Common ; if not, don't draw and branch
lda metatile_bytes_common, X ; save top-left tile of the metatile in the ppu
sta PPUDATA
lda metatile_bytes_common+2, X ; save bomttom-left tile of the metatile in the ppu
sta PPUDATA
jmp @RLE_Goto_Next_Row ; we drew our mtt in this row, since we're drawing just one column, we no longer need to draw more tiles in this row
@RLE_Loop_Skip_Draw_Common:
lda aux4
cmp #$FF ; we check if we drew all the common tiles compressed in the byte
bne @RLE_Loop_Draw_Common ; theoretically, this branching will always happen
@RLE_Loop_After_Byte_Reading:
iny ; get next byte in row
beq @RLE_End ; we read all bytes in the column
jmp @RLE_Loop_Bytes_in_Row ; we didn't read all the bytes in the column, go to next row in the column
@RLE_Goto_Next_Row:
lda aux1
lsr ; this needs to happen because aux1 is multiplied by 2 because each row has 2 bytes
cmp #15
beq @RLE_End
jmp @RLE_Rows_Loop
@RLE_End:
; ending
lda genericflags1
eor #%00000001 ; alternate between column 0 or 1 to be drawn
sta genericflags1
;bit PPUSTATUS
lda #$00 ; set back to increment +1 mode
ora ppuctrl_mirror
sta PPUCTRL
rts
.endproc
Re: Scrolling RPG maps
If all you want is to set the increment mode for doing updates, you don't really need to preserve the other bits during the update process itself, but you do need to restore all the bits *after* the update is done, before the rendering of the new frame starts.
These problems are most likely related. You're probably taking more time than the length of vblank to do your updates, so the frame starts with a bad scroll and the final vram writes fail.1) the map "shaking" while moving (this effect is barely visible on the video unfortunately)
2) the new column on the right is only half drawn. All the rest is black. I checked with the debugger and it seems like the routine is storing the correct bytes on PPU_DATA up until the end of the column.
Do not decompress data or do any sort of slow processing during vblank. The vertical blank interval is really short, so you should use it for copying already processed and buffered data to vram, otherwise you won't be able to update much data at all before the time is up.It also has to decompress the tiles.
BTW, we can't watch the video without having to request access.