Page 1 of 2

Displaying Multiple Sprites

Posted: Tue Jun 27, 2006 5:32 pm
by Cypherr
I am having some problems having more than one sprite display at the same time. I have no problem displaying the first one, but as soon as I attempt to display more than one, none of the sprites are displayed.

Code: Select all

	.inesprg 1   
	.ineschr 1   
	.inesmir 1 
	.inesmap 0 

	.bank 1     
	.org $FFFA  

	.dw 0        
	.dw start         
	.dw 0       
                     
	.bank 0  
	.org $8000  

start:

	lda #%00001000 
	sta $2000
	lda #%00011110
	sta $2001

	jsr waitblank	
	jsr waitblank	

	lda #$3F  
	sta $2006
	lda #$00
	sta $2006
	
	ldx #$00

loadpal:                
	lda tilepal, x
	sta $2007     
	inx           
	cpx #32        
	bne loadpal    
	ldx #$00 

mainloop:
	jsr waitblank
	jsr drawsprite
	;jsr joystick
	jmp mainloop

waitblank:   
	lda $2002  ; load A with value at location $2002
	bpl waitblank  ; if bit 7 is not set (not VBlank) keep checking

	rts

drawsprite:
	lda #$00	
	sta $2003	
	sta $2003
	
	lda #$50	
	sta $2004	
	lda #$00	
	sta $2004	
	lda #%00000000 
	sta $2004	
	lda #$50
	sta $2004	

	lda #$08
	sta $2004
	lda #$00
	sta $2004
	lda #%00000000 
	sta $2004
	lda #$18
	sta $2004

	rts

tilepal: ;.incbin "test.pal" 
	.db $3F,$00,$10,$30,$3F,$00,$10,$30,$3F,$00,$10,$30,$3F,$00,$10,$30
	.db $3F,$00,$10,$30,$3F,$00,$10,$30,$3F,$00,$10,$30,$3F,$00,$10,$30 

	.bank 2      
	.org $0000   
	.incbin "testbkg.chr"
	.incbin "testspr.chr"

Is there anything in my code that is outright wrong? Any help to point me in the right direction or figure out why I can not display more than one sprite would be appreciated.

If I comment out the second part of the drawsprite routine - one sprite will appear.

Code: Select all

lda #$08
	sta $2004
	lda #$00
	sta $2004
	lda #%00000000 
	sta $2004
	lda #$18
	sta $2004

However, with that code not-commented out, neither of the sprites will show. I am new to NES development so I'm sorry if the answer to my problem is obvious.

Thanks

Posted: Tue Jun 27, 2006 5:46 pm
by Celius
I only tried using that method of sprite output like once. Doing it that way isn't very efficiant, so you should try using sprite DMA. What you do, is you take a section in RAM, $100 bytes, and store the sprite data the same way in that section. Say, take $500-$5FF, and store your sprite data like so:

$500 - Sprite 0 Y pos
$501 - Sprite 0 Tile #
$502 - Sprite 0 Attribute
$503 - Sprite 0 X pos

$504 - Sprite 1 Y pos
$505 - Sprite 1 Tile #
....

Okay, so you get that? And now, in your NMI routine, or once every Vblank, store #5 in $4014, because #5 is the high byte of $500. Do you understand?

Posted: Tue Jun 27, 2006 6:11 pm
by Cypherr
thanks for the reply.

i think i understand. but now i am getting three sprites displayed instead of the two i expect. this may be something i am overlooking but i don't see why there is there.

the two sprites i expect to show up are at 120,120 and 8,18 as expected. however, there is another one at 0,0.

Code: Select all

sprite_x = 0 
sprite_y = 1 


	.inesprg 1 
	.ineschr 1   
	.inesmir 1  
	.inesmap 0  

	.bank 1     
	.org $FFFA  

	.dw 0       
	.dw start    
		     
	.dw 0       
                    


	.bank 0  
	.org $8000  

start:

	lda #%00001000 
	sta $2000
	lda #%00011110
	sta $2001

	jsr init_vars

	jsr waitblank	
	jsr waitblank

	lda #$3F 
	sta $2006
	lda #$00
	sta $2006
	
	ldx #$00

loadpal:                
	lda tilepal, x  
	sta $2007       
	inx             
	cpx #32         
	bne loadpal     
	ldx #$00 


mainloop:
	jsr waitblank
	jsr drawsprite
	;jsr joystick
	jmp mainloop

	
waitblank:   
      
	lda $2002 
	bpl waitblank  

	rts

drawsprite:
	
	lda #5
	sta $4014
	rts

	
init_vars:
	lda #120
	sta sprite_x
	sta sprite_y

	lda sprite_y  	
	sta $500	
	lda #$00	
	sta $501	
	lda #%00000000 
	sta $502	
	lda sprite_x
	sta $503	

	lda #$08
	sta $504
	lda #$00
	sta $505
	lda #%00000000 
	sta $506
	lda #$18
	sta $507
	

	rts
	
tilepal: ;.incbin "test.pal" ; include and label our pallete 
	.db $3F,$00,$10,$30,$3F,$00,$10,$30,$3F,$00,$10,$30,$3F,$00,$10,$30
	.db $3F,$00,$10,$30,$3F,$00,$10,$30,$3F,$00,$10,$30,$3F,$00,$10,$30 

	.bank 2       
	.org $0000   
	.incbin "testbkg.chr"
	.incbin "testspr.chr"


Any additional help is appreciated and thanks again for your initial help

Posted: Tue Jun 27, 2006 6:36 pm
by never-obsolete
DMA writes 255 bytes whether all are being used or not. so it still writes repetive zeros. you need to either:

1. set tile $00 in the sprite pattern table to be blank so that all the unused sprites are blank by default

or

2. set the tile data of each unused sprite in the DMA to whatever tile in your pattern table is blank

option 1 would be the easiest.

Posted: Tue Jun 27, 2006 6:39 pm
by Celius
Easy enough to explain, I believe.

You must have a something drawn in tile #0 in your pattern tables. You see that the rest of the sprites are all just $00s and $FFs, right? Because you didn't do anything to the rest of the RAM that's being stored into $4014. If it's all 00s, you are storing sprite with tile #0 at 0,0 with attribute 0. So of course the sprite will show up at the top corner of the screen.

I actually was wondering that at one point, and then it dawned on me that that was the problem. So if you don't want it to be seen, just move the stuff that's in tile #0 in your pattern tables. There will always be 64 sprites on the screen with this method, there are complicated ways around this, But most games always have 64 sprites on screen, as most games use sprite DMA. Does that help?

Edit: Oh, never-obsolete answered before me! Oh well, it's best to have 2 explanations anyways.

Posted: Tue Jun 27, 2006 6:50 pm
by Cypherr
thanks for both your replies. definitely makes sense to me now.

i was trying to understand the whole sprite dma all day today, but all it took was a matter of minutes from you guys. thanks

Posted: Tue Jun 27, 2006 7:00 pm
by Quietust
Actually, the proper way to hide unused sprites is to set their Y-coordinate to any value between $F0 and $FF (inclusive), thereby placing it off the bottom of the screen.

(edit: F0-F7 is also valid; technically, EF is too)

Posted: Tue Jun 27, 2006 7:32 pm
by Celius
Well, if you are testing on the real NES, you won't be able to see sprites in the corner, will you? Actually, at least not the whole thing, the bottom 8 pixels, correct? I would just stick with not using tile #0. If you want to use it for sprite #0 hits, just put a line of pixels on the top of the tile, so you won't be able to see it on the real system, but it still is usable for doing that.

Posted: Tue Jun 27, 2006 7:55 pm
by Disch
Celius wrote:Well, if you are testing on the real NES, you won't be able to see sprites in the corner, will you?
But you'll overflow sprites on that scanline, destroying the possibility of using $2002.5 (not like that's paticularly useful). Plus any sprite you WANT visible on the top of the screen might not be, since the other sprites may be found first. Plus then you have to keep a blank tile in CHR at all times, which you might not want to do with sprites (kind of a waste of a tile).

It's better all around to do what Q said and just put the sprites off-screen.

Posted: Tue Jun 27, 2006 8:31 pm
by Celius
Oh! I forgot, the screen is shorter than it is wide, so yeah, them not being on screen is a better idea.

Posted: Tue Jun 27, 2006 10:40 pm
by Dwedit
Depending on your TV, you might see the top 8 pixels, or you might not.

Posted: Wed Jun 28, 2006 8:50 am
by Bregalad
I think full the whole SPR RAM with $f0 is a proper way to blank it.
Then, setup all sprites you need, and if you don't use all 64 sprites, wich is most likely to be the case, unused sprites will never be fetch, since the last scanline is 240, wich is $f0 in hexadecimal. Since sprites are fetched one scanline late, sprites with $f0 as their vertical position will never be fetch, and so it is technically as if they wouldn't exist at all.
$f1-$ff will work as well, if you prefer $f8, $ff or whathever for any reason, go for it.

Posted: Wed Jun 28, 2006 9:41 am
by Memblers
Dwedit wrote:Depending on your TV, you might see the top 8 pixels, or you might not.
That's true, with the programs I've designed I just consider the top and bottom 8 lines to be a loss. I've played on some bad TVs, so I can see why not to put anything important on the edges.

Posted: Wed Jun 28, 2006 10:40 am
by tepples
Celius wrote:Well, if you are testing on the real NES, you won't be able to see sprites in the corner, will you?
In Europe you can, as European TVs have more visible scanlines. The NES image is always 240 scanlines tall, but an image 240 scanlines tall on a PAL TV is as tall as something 200 scanlines tall on an NTSC or PAL60 TV: easily within the safe area.

Posted: Wed Jun 28, 2006 10:47 am
by Bregalad
Most commercial NES games were devlopped assuming that the top and bottom 8 pixels are not visible to the user, causing graphical glitches in PAL.
For this reason, I think have emulator hide top and bottom borders is a good thing, even if it doesn't really emulate the hardware, blah, blah, blah, it is looking what most games were designed for.