Mildanner
Newcomer
Original guide by Mildanner
In Sonic 2, the HUD has a complex code to begin with. If you want to take a look how it looks like, it also loads the sprites and specific. In Sonic 2, the only difference the HUD is popping once the Title Cards leave, which is the one reason why it doesn't look perfect.
In this guide, you will learn how to move a object builder like HUD and how to make the HUD moving every time it loads a level (Only works with 1P).
Go to your s2.asm from Sonic 2 disassembly file, find the BuildHUD: label. Replace the entire routine until ; End of function BuildHUD with this
Done! Now your hack has a fellow HUD scrolling.
and below it. Insert this
Note: Sonic 2 has a very different code than Sonic 1, we ported this code to work with Sonic 2. Which gave the same results as the original does.
Good luck with your hack having a sound effect once the HUD scrolling is finished, I hope you enjoy this tutorial.
In Sonic 2, the HUD has a complex code to begin with. If you want to take a look how it looks like, it also loads the sprites and specific. In Sonic 2, the only difference the HUD is popping once the Title Cards leave, which is the one reason why it doesn't look perfect.
In this guide, you will learn how to move a object builder like HUD and how to make the HUD moving every time it loads a level (Only works with 1P).
Go to your s2.asm from Sonic 2 disassembly file, find the BuildHUD: label. Replace the entire routine until ; End of function BuildHUD with this
Code:
BuildHUD:
; --- HUD BLINKING LOGIC ---
moveq #0,d1 ; Clear d1 (Default mapping frame: no flashing)
tst.w (Ring_count).w ; Check if total rings equal 0
bne.s .checkMinuteOnly ; If rings > 0, branch to check minutes only
; If rings equal 0, process ring counter flashing
btst #3,(Level_frame_counter+1).w ; Test frame counter bit for blink frequency
bne.s .checkMinuteWithRing ; If bit is set, hide ring text (skip frame increment)
addq.w #1,d1 ; Set mapping frame to flash rings index
.checkMinuteWithRing:
cmpi.b #9,(Timer_minute).w ; Has the game timer reached 9 minutes?
bne.s .calculateScroll ; If not, branch directly to scrolling calculation
addq.w #2,d1 ; Set mapping frame to double flash (Time + Rings)
bra.s .calculateScroll
.checkMinuteOnly:
btst #3,(Level_frame_counter+1).w ; Test frame counter bit for time flashing
bne.s .calculateScroll ; If bit is set, skip flashing adjustment
cmpi.b #9,(Timer_minute).w ; Has the game timer reached 9 minutes?
bne.s .calculateScroll ; If not, branch directly to scrolling calculation
addq.w #2,d1 ; Set mapping frame to flash time only
; ===========================================================================
.calculateScroll:
; --- DYNAMIC SLOW SCROLLING LOGIC (3 PIXELS PER FRAME) ---
move.w (Level_frame_counter).w,d3 ; Read global level frame counter (starts at 0)
mulu.w #3,d3 ; Multiply frame count by 3 for a steady scroll rate
; --- TOTAL SLIDING TRAVEL DISTANCE ---
cmpi.w #112,d3 ; Has the scroll distance hit or exceeded 112 pixels?
blt.s .applyOffset ; If less than 112, continue tracking movement
cmpi.w #114,d3 ; Is this the very first frame it reached the end?
bgt .lockValue
.lockValue:
moveq #112,d3 ; Clamp the scroll register permanently at 112
.applyOffset:
subi.w #112,d3 ; Convert the climbing frame scale into negative entry displacement
; --- X/Y COORDINATE CONFIGURATION ---
addi.w #spriteScreenPositionX(16),d3 ; Apply base X-macro position (compensated for extended scroll)
move.w #spriteScreenPositionYCentered(24),d2 ; Apply centered Y-axis row position
; --- SPRITE RENDER ENGINE ---
lea (HUD_MapUnc_40A9A).l,a1
movea.w #make_art_tile(ArtTile_ArtNem_HUD,0,1),a3
add.w d1,d1 ; Multiply frame layout index by 2 (Word sizing)
adda.w (a1,d1.w),a1 ; Fetch chosen mapping vector depending on flash state
move.w (a1)+,d1 ; Read the direct sprite piece array count
subq.w #1,d1 ; Subtract 1 to accommodate the core internal DBF loop
bmi.s .exit ; If sprite list is empty, abort safely
jsrto JmpTo_DrawSprite_Loop ; Execute standard Sonic 2 VDP sprite compilation loop
.exit:
rts
; End of function BuildHUD
(Optional) Once position is reached to play sound effect
If your hack looks clear with a fellow HUD scrolling and it has no sound, you can simply go to the line which that one code is this
Code:
bgt .lockValue
Code:
move.w #SndID_TallyEnd,d0 ; Load the sound ID
jsr (PlaySound).l ; Play the sound effect exactly once
Good luck with your hack having a sound effect once the HUD scrolling is finished, I hope you enjoy this tutorial.
Last edited: