Scrolling RPG maps

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

User avatar
zanto
Posts: 57
Joined: Sun Mar 07, 2021 11:15 pm
Location: Rio de Janeiro, Brazil

Re: Scrolling RPG maps

Post by zanto »

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.
Pokun
Posts: 2681
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: Scrolling RPG maps

Post by Pokun »

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.
turboxray
Posts: 348
Joined: Thu Oct 31, 2019 12:56 am

Re: Scrolling RPG maps

Post by turboxray »

zanto wrote: Sat Apr 03, 2021 1:37 pm But to be honest, I'm not a fan of having to store RAM memory for this, I don't like this idea.
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).
Pokun
Posts: 2681
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: Scrolling RPG maps

Post by Pokun »

Why would PC-Engine be any different?
User avatar
Gilbert
Posts: 564
Joined: Sun Dec 12, 2010 10:27 pm
Location: Hong Kong
Contact:

Re: Scrolling RPG maps

Post by Gilbert »

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.
Pokun
Posts: 2681
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: Scrolling RPG maps

Post by Pokun »

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).
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: Scrolling RPG maps

Post by lidnariq »

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.
User avatar
zanto
Posts: 57
Joined: Sun Mar 07, 2021 11:15 pm
Location: Rio de Janeiro, Brazil

Re: Scrolling RPG maps

Post by zanto »

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):

Code: Select all

LDA #%00000100        ; set to increment +32 mode, don't care about other bits
	STA PPUCTRL
Any ideas what I could be doing wrong this time? :cry:
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: Scrolling RPG maps

Post by lidnariq »

Are you sure you don't care about the other bits?

Especially, say, the NMI bit?
Pokun
Posts: 2681
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: Scrolling RPG maps

Post by Pokun »

You can use ORA with #%00000100 to care about the other bits.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Scrolling RPG maps

Post by tokumaru »

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.
turboxray
Posts: 348
Joined: Thu Oct 31, 2019 12:56 am

Re: Scrolling RPG maps

Post by turboxray »

Pokun wrote: Sun Apr 04, 2021 10:11 am Why would PC-Engine be any different?
Because you don't need to wait until vblank to update sprite entries (SAT) or tile maps on the PCE. You can write/read vram during active display freely.
Pokun
Posts: 2681
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: Scrolling RPG maps

Post by Pokun »

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:

Code: Select all

  lda ppuctrl_buffer
  ora #%00000100       ; set to increment +32 mode, do care about other bits
  sta ppuctrl_buffer
  sta PPUCTRL
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.
User avatar
zanto
Posts: 57
Joined: Sun Mar 07, 2021 11:15 pm
Location: Rio de Janeiro, Brazil

Re: Scrolling RPG maps

Post by zanto »

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)

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
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Scrolling RPG maps

Post by tokumaru »

zanto wrote: Mon Apr 05, 2021 3:07 pmAh yeah, keeping a copy of the previous ppuctrl bits and using it solved the freezing problem.
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.
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.
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.
It also has to decompress the tiles.
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.

BTW, we can't watch the video without having to request access.
Post Reply