[4.1.5] Implementing a basic Sprite HUD

dale_coop

Moderator
Staff member
When I was playing with the new version (4.5 from the Summer Camp tutorials), I thought that the new sprite HUD for scrolling games was really cool.
No more Sprite Zero settings and really less slowdowns on your scrolling games! (the normal hud is really CPU consuming)


IMPORTANT: If you haven't seen that video explaining that new sprite Hud, you can watch it here (please watch it before going further): https://vimeo.com/432579544/67c856e297


Let's implement the same Sprite HUD system for NESmaker 4.1.5!

spriteHud.gif

(myHealth and myLives are displayed as a sprite Hud)


Here's how to do:

1) First, we need to disable the normal HUD (tile based). Go to the "Hud & boxes" settings:
- uncheck the "Use Sprite Zero Detection" (you don't need that anymore)
- click on the Hud area "SET" button until it disappears (X:0 Y:0 W:0 H:0)

2020-07-17-01-14-29-NES-MAKER-4-1-5-Version-0x159-Plat-MST.png



2) In the "Project Settings > User Constants", change the "HIDE_HUD" constant value to "1" (to disable the normal HUD):

2020-07-16-21-37-52-Project-Settings.png



3) In Windows Explorer, go to your "NESmaker" folder, then navigate until the "GameEngineData\Routines\Basic\System\Macros\" sub folder, make a new file named "DrawSpriteHud.asm" (or just duplicate any file from that folder and rename it):

2020-07-17-01-05-31-Macros.png


Modify that script (in Notepad++ or Notepad...), and replace all the content with that code:

Code:
MACRO DrawSpriteHud arg0, arg1, arg2, arg3, arg4, arg5, arg6
	; arg0 = starting position in pixels, x
	; arg1 = starting position in pixels, y
	; arg2 = sprite to draw, CONTAINER
	; arg3 = MAX	
	; arg4 = sprite to draw, FILLED
	; arg5 = variable.
	; arg6 = attribute
	
	tya
	PHA
	TXA
	PHA
	
	LDA arg4
	STA temp ;; full
	LDA arg0
	STA temp2
	
	LDA arg5
	BNE +notZeroVar 
		;; if the variable was zero, draw empty tiles
		LDA arg2
		STA temp 
	+notZeroVar:

	LDX #$00
	doDrawSpriteHudElementFullLoop:
		LDY spriteOffset
		LDA arg1
		STA SpriteRam,y
		INY
		LDA temp
		STA SpriteRam,y
		INY
		LDA arg6
		STA SpriteRam,y
		INY
		LDA temp2
		STA SpriteRam,y
		INY
	
		LDA spriteOffset
		CLC
		ADC #$04
		STA spriteOffset
		LDA temp2
		CLC
		ADC #$08
		STA temp2
		
		INX
		CPX arg3 ;; is it to the max?
		BEQ doneWithSpriteDrawElement
			CPX arg5 ;; has it reached the fill point?
			BCS changeToContainerSprite
				JMP doDrawSpriteHudElementFullLoop
			changeToContainerSprite:
			
					;; not done with sprite draw element, but now need to change to container
					LDA arg2
					STA temp
					JMP doDrawSpriteHudElementFullLoop
			doneWithSpriteDrawElement:
		
	PLA
	TAX
	PLA
	TAY
	ENDM


4) Still, in Windows Explorer, go to your "NESmaker" folder, then navigate until the "GameEngineData\Routines\Basic\ModuleScripts\MainScripts\" sub folder, make a new file named "PreDraw_WithSpriteHud.asm" (or just duplicate any file from that folder and rename it, like this):

2020-07-17-00-55-39-Main-Scripts.png


Modify that script (in Notepad++ or Notepad...), and replace all the content with that code:

Code:
;; sprite pre-draw
;;; this will allow a user to draw sprites before object sprites are drawn.
;;; keep in mind, there are still only 64 sprites that can be drawn on a screen, and still only 8 per scan line!

LDA gameState
CMP #GS_MainGame				;; we check if on main game screens
BEQ +							;; if it is we can continue
	JMP skipDrawingSpriteHud	;; if not we skip the sprite Hud drawing
+
LDA screenFlags
AND #%00000001					;; we check if the "hide hud" screen flag is set
BEQ +							;; if not we can continue
	JMP skipDrawingSpriteHud	;; if it is we skip the sprite Hud drawing
+

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;; TO KNOW HOW TO USE THAT SPRITE HUD, CHECK JOE'S VIDEO:
	;; https://vimeo.com/432579544/67c856e297
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	
	;DrawSpriteHud #$08, #$08, #$11, #$10, #$10, myVar, #$00
		; arg0 = starting position in pixels, x
		; arg1 = starting position in pixels, y
		; arg2 = sprite to draw, CONTAINER
		; arg3 = MAX	
		; arg4 = sprite to draw, FILLED
		; arg5 = variable.
		; arg6 = attribute

	DrawSpriteHud #$10, #$10, #$0C, #$03, #$0D, myHealth, #$01

	DrawSpriteHud #$D0, #$10, #$0E, #$03, #$0F, myLives, #$00



skipDrawingSpriteHud:


5) In your "Project Settings > Script Settings", select the "Handle Sprit Pre-Draw" element and assign the "PreDraw_WithSpriteHud.asm" script we just made:

2020-07-17-01-01-44-Project-Settings.png




Voilà, now you should have the same Sprite Hud system than the new version but on your 4.1.5 project.
 

mouse spirit

Well-known member
Perfect dale_coop. Ofcourse i have an issue. I had to comment out the 2 ;JMP skipDrawingSpriteHud;'s to get it to work.Not sure why.I did have hide hud set to 1,truly i have all my screens already hiding hud. So i tried to leave that constant at 0 and try it, and at 1 and try it.
Anyway it works great so far and i love it. It's just that i need it to hide on the start screen and other screens. I will mess with it.

Edit: only commented out the 2nd ;JMP skipDrawingSpriteHud. The mainscreen check is working fine. Must be the hide hud check. Maybe because i already hid huds?


Editerio: Yes so it seems when you have already hidden hundreds of huds... you have to unhide them for each screen and make the constant 1. Or.... comment that line out. Would that be ok ya think, just comment that 2nd check out? I'm testing it and seems ok, as long as you have all individual screens with hide hud ticked.

gaxcbnxme.png
 

dale_coop

Moderator
Staff member
But what if on certains screens you don’t want to draw the hud... for example, for cutscenes or special game over screen...

So, I think that situation, you would check the screen flag “hide hud”.

The code you commented out was meant to deal with those cases... the screens where you don’t want to have any hud drawn!

And for the screens you want sprite hud, I check the “hide hud” on the screen Infos dialog.
Yeah, I am talking about the screen “Infos” dialog, not the global user constant ;)
 

foovax

New member
I'm running 4.5.6 and the MetroVania engine?

I was trying to "Hide HUD" on the title screen which wouldn't work since it's using the sprite hud.

Finding this thread helped me to hide it on my StartScreen but I also ran into the same issue related to Hide HUD not being proper label.

Routines\BASE_4_5\Game\MOD_MetroidVania\Common\doDrawSpriteHud_metroidvania.asm(20): Unknown label.

With line 20 being: LDA screenFlags

Searching the asm code I see references of ScreenFlags00 but I'm not sure where all of this is clearly defined.

I believe this is the reason the second check doesn't work because the flag naming isn't consistent across all tutorials/engines?

Any assistance is appreciated.
 

SciNEStist

Well-known member
Thank you so much for this! As it was with my game, I was going to have to pick between the slowness of the zero sprite hud, or dealing with the hud not working every other screen. Now I can keep working without the hud issues making me so worried about the final product.
 

crazygrouptrio

Active member
Is there a way to make the hud sprites vertical instead of horizontal? (Mega man style)
Also is it possible to add a check for a half-filled sprite? So like 1 damage would take it down a half and then another would take it down a full? I've tried but can't get it to work. It always changes all of them instead of just the last one 😅
 

CutterCross

Active member
Is there a way to make the hud sprites vertical instead of horizontal? (Mega man style)
Also is it possible to add a check for a half-filled sprite? So like 1 damage would take it down a half and then another would take it down a full? I've tried but can't get it to work. It always changes all of them instead of just the last one 😅

This will work for a vertical sprite HUD, just having each subsequent sprite drawn with a vertical offset instead of horizontal:
Code:
MACRO DrawSpriteHud arg0, arg1, arg2, arg3, arg4, arg5, arg6
    ; arg0 = starting position in pixels, x
    ; arg1 = starting position in pixels, y
    ; arg2 = sprite to draw, CONTAINER
    ; arg3 = MAX   
    ; arg4 = sprite to draw, FILLED
    ; arg5 = variable.
    ; arg6 = attribute
    
    TYA
    PHA
    TXA
    PHA
    
    LDA arg1
    STA temp2    ;; stores vertical position in a temp variable so we can change it during the draw loop.
    
    LDA arg4
    STA temp ;; full
    
    LDA arg5
    BNE +notZeroVar
        ;; if the variable was zero, draw empty tiles
        LDA arg2
        STA temp
    +notZeroVar:

    LDX #$00
    doDrawSpriteHudElementFullLoop:
        LDY spriteOffset
        LDA temp2
        STA SpriteRam,y
        INY
        LDA temp
        STA SpriteRam,y
        INY
        LDA arg6
        STA SpriteRam,y
        INY
        LDA arg0
        STA SpriteRam,y
        INY
    
        LDA spriteOffset
        CLC
        ADC #$04
        STA spriteOffset
        LDA temp2
        CLC
        ADC #$08
        STA temp2
        
        INX
        CPX arg3 ;; is it to the max?
        BEQ doneWithSpriteDrawElement
            CPX arg5 ;; has it reached the fill point?
            BCS changeToContainerSprite
                JMP doDrawSpriteHudElementFullLoop
            changeToContainerSprite:
            
                    ;; not done with sprite draw element, but now need to change to container
                    LDA arg2
                    STA temp
                    JMP doDrawSpriteHudElementFullLoop
            doneWithSpriteDrawElement:
        
    PLA
    TAX
    PLA
    TAY
    ENDM

(Also I gotta say, not a big fan of how large the new code blocks are compared to the older forums.)
 

crazygrouptrio

Active member
This will work for a vertical sprite HUD, just having each subsequent sprite drawn with a vertical offset instead of horizontal:
Code:
MACRO DrawSpriteHud arg0, arg1, arg2, arg3, arg4, arg5, arg6
    ; arg0 = starting position in pixels, x
    ; arg1 = starting position in pixels, y
    ; arg2 = sprite to draw, CONTAINER
    ; arg3 = MAX 
    ; arg4 = sprite to draw, FILLED
    ; arg5 = variable.
    ; arg6 = attribute
  
    TYA
    PHA
    TXA
    PHA
  
    LDA arg1
    STA temp2    ;; stores vertical position in a temp variable so we can change it during the draw loop.
  
    LDA arg4
    STA temp ;; full
  
    LDA arg5
    BNE +notZeroVar
        ;; if the variable was zero, draw empty tiles
        LDA arg2
        STA temp
    +notZeroVar:

    LDX #$00
    doDrawSpriteHudElementFullLoop:
        LDY spriteOffset
        LDA temp2
        STA SpriteRam,y
        INY
        LDA temp
        STA SpriteRam,y
        INY
        LDA arg6
        STA SpriteRam,y
        INY
        LDA arg0
        STA SpriteRam,y
        INY
  
        LDA spriteOffset
        CLC
        ADC #$04
        STA spriteOffset
        LDA temp2
        CLC
        ADC #$08
        STA temp2
      
        INX
        CPX arg3 ;; is it to the max?
        BEQ doneWithSpriteDrawElement
            CPX arg5 ;; has it reached the fill point?
            BCS changeToContainerSprite
                JMP doDrawSpriteHudElementFullLoop
            changeToContainerSprite:
          
                    ;; not done with sprite draw element, but now need to change to container
                    LDA arg2
                    STA temp
                    JMP doDrawSpriteHudElementFullLoop
            doneWithSpriteDrawElement:
      
    PLA
    TAX
    PLA
    TAY
    ENDM

(Also I gotta say, not a big fan of how large the new code blocks are compared to the older forums.)
This works but its backwards 😅 health is removed bottom to top.
 

CutterCross

Active member
This works but its backwards 😅 health is removed bottom to top.

Then you should just be able to change
Code:
        LDA spriteOffset
        CLC
        ADC #$04
        STA spriteOffset
        LDA temp2
        CLC
        ADC #$08
        STA temp2

To this:
Code:
        LDA spriteOffset
        CLC
        ADC #$04
        STA spriteOffset
        LDA temp2
        SEC
        SBC #$08
        STA temp2
 

crazygrouptrio

Active member
Then you should just be able to change
Code:
        LDA spriteOffset
        CLC
        ADC #$04
        STA spriteOffset
        LDA temp2
        CLC
        ADC #$08
        STA temp2

To this:
Code:
        LDA spriteOffset
        CLC
        ADC #$04
        STA spriteOffset
        LDA temp2
        SEC
        SBC #$08
        STA temp2
Ah yes, I changed both of those ADCs to SBCs, and that sure didnt work 😅 now if I can get it to display halves this should save me some code space from what I had before (and be a heck of a lot easier to manage)
 

CutterCross

Active member
Ah yes, I changed both of those ADCs to SBCs, and that sure didnt work 😅 now if I can get it to display halves this should save me some code space from what I had before (and be a heck of a lot easier to manage)
You could change the y-offset to #$04 and overlap sprites to fake "half" values, though it'll take the same amount of sprites and will cause the HUD to use at least 2 sprites per scanline.
 
Top Bottom