[4.5.6] Duplicate Monster Fix

AllDarnDavey

Active member
An issue I kept running into in the MetroidVania Module was I would occasionally get duplicate monsters loaded into the same spot. It's not always easy to tell, especially if their AI behavior is in sync, but it can cause some major slowdown as what looks like a single enemy is actually two on top of each other (and I've even seen three stacked up). If your duplicate enemies do things like fire projectiles then it'll tank performance even faster. Below is an example of what looks like 2 enemies, but in fact is 4, Mesen's Sprite Viewer shows the double stack of enemies, but the slowdown is immediately noticeable.
duplicateLoadedMonsters.gif

What's happening is when the camera seam (1/2 a screen width offscreen in either direction) hits the enemy load position, the enemy is loaded and prepped to activate once visible. If you scroll back and forth a bit (easy to do while wall jumping or dodging bullets), your scroll back might not be far enough to trigger unloading the enemy, and then when you scroll forward again, a second duplicate enemy is loaded on the same spot.

To fix this I made a change to the doUpdateCamera routine bit of code that loads the monsters, so it double checks to see if there is already a monster loaded into that X position first. I could've had it check for duplicate X position, then also check Y position, but I only did an X check to save a few CPU cycles. It does mean that my monsters have to be set at least one tile apart horizontally, but I'm okay with that -- in fact it makes it easy to see if the script is working, if I put 2 monsters in the same X position and 1 doesn't load, I know the script is doing it's job.
MonsterXoffset.PNG

Just replace the checkSeamForMonsterPosition section of your doUpdateCamera.asm routine script (it's should be right at the end), with this version.

Code:
checkSeamForMonsterPosition:
    ;; y is loaded before subroutine.
            LDA (pointer6),y
            STA temp
            ASL
            ASL
            ASL
            ASL
            STA temp2
            LDA scrollUpdateColumn
            AND #%11110000
            CMP temp2
            BNE +noMonsterToLoadInThisColumn
            ;;check if a monster is already loaded in that x position first
            LDX #$00
            monsterDupeCheck:
                LDA Object_x_hi,x
                CMP temp2
                BEQ +noMonsterToLoadInThisColumn              
                INX              
                CPX #TOTAL_MAX_OBJECTS
                BNE monsterDupeCheck          
           
                LDA temp
                AND #%11110000
                STA temp1
                CMP #%11110000
                BEQ +noMonsterToLoadInThisColumn
                    CreateObjectOnScreen temp2, temp1, tempD, #$00, scrollUpdateScreen
            +noMonsterToLoadInThisColumn:
    RTS

EDIT: DON'T REPLACE THE ENTIRE FILE WITH THIS CODE, JUST THE LAST SECTION
 

dale_coop

Moderator
Staff member
That is VERY interesting. I noticed that issue in my son's byte off demo when I played.
I will check that.
Thank you, AllDarnDavey for sharing it <3
 

Jonny

Well-known member
Thank you @AllDarnDavey

This fixed my pickups from duplicating. I'm getting some strange problems though. When I return to the same screen, I seem to get random results. Sometime only 1 pickup spawns, sometimes none, sometimes 2. It seems to depend on how many screens I've done past the objects before returning and the direction I approach them in. It's really odd.
 

Jonny

Well-known member
Think I've solved my issue...
I commented out the max objects check and it seems to work so far for Metrovania module.

Code:
;;CPX #TOTAL_MAX_OBJECTS
;;BNE monsterDupeCheck
 

vanderblade

Active member
Update: So it appears I have a strange problem. Like @9Panzer, I'm using the metroidvania module, but for some reason, if I comment out #TOTAL_MAX_OBJECTS / BNE monsterDupeCheck it negates the purpose of the code: I still get duplicate monsters. Yet if I leave this uncommented out, certain monsters just do not appear where I have placed them. @9Panzer, did you run into any issues with the above "fix" or was everything fine from the get-go? I have no idea what the issue might be.

Where is monsterDupeCheck, by the way? That code isn't in my DoUpdateCamera default script. Perhaps that's the issue?
 

AllDarnDavey

Active member
Yet if I leave this uncommented out, certain monsters just do not appear where I have placed them.
The duplicate check looks to see if two monsters have the same X position. So if you have one monster on top of another it'll cull one thinking it's a duplicate. This might be causing your issue if your monsters are stacked vertically. If that is what is causing your issue, to fix it you'd need to either not stack monsters vertically, or make the code a bit more expensive and add a Y position check after the X position check, so it only culls monsters that are "true" duplicates.
 

tbizzle

Well-known member
I have been having the problem of no monsters spawning at all after I lose a life and I'm teleported back to the start screen. Bummer too, like at least 50% of them.
 

baardbi

Well-known member
Think I've solved my issue...
I commented out the max objects check and it seems to work so far for Metrovania module.

Code:
;;CPX #TOTAL_MAX_OBJECTS
;;BNE monsterDupeCheck
This solved the problem with items and monsters not spawning. However... Now there are duplicate monsters (which I didn't have before) lol.
 
I have changed it to check only against the object being created.
Seem to be working fine for now but may need further testing, thanks @AllDarnDavey.

**Edited**
After more testing, it does not work.

Code:
checkSeamForMonsterPosition:
    ;; y is loaded before subroutine.
            LDA (pointer6),y
            STA temp
            ASL
            ASL
            ASL
            ASL
            STA temp2
            LDA scrollUpdateColumn
            AND #%11110000
            CMP temp2
            BNE +noMonsterToLoadInThisColumn     
            ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;Prevent monster duplicate
                LDX tempD
                LDA Object_x_hi,x
                CMP temp2
                BEQ +noMonsterToLoadInThisColumn 
            ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                LDA temp
                AND #%11110000
                STA temp1
                CMP #%11110000
                BEQ +noMonsterToLoadInThisColumn
                    CreateObjectOnScreen temp2, temp1, tempD, #$00, scrollUpdateScreen
            +noMonsterToLoadInThisColumn:
    RTS
 
Last edited:

tbizzle

Well-known member
I'm still keeping a candle lit on this one. I really hope someone solves this some day!!
 
Last edited:
Firstly Thanks @AllDarnDavey for this. I had implemented it into my metrovania project quite a while ago but still had issues with duplicates spawning even when not vertically stacked. I have been in bug fixing mode and did this.....
I think I added the Y check correctly. It works for me in 4.5.9

Code:
checkSeamForMonsterPosition:
    ;; y is loaded before subroutine.
            LDA (pointer6),y
            STA temp
            ASL
            ASL
            ASL
            ASL
            STA temp2
            LDA scrollUpdateColumn
            AND #%11110000
            CMP temp2
            BNE +noMonsterToLoadInThisColumn
            ;;check if a monster is already loaded in that x position first
            LDX #$00
            monsterDupeCheckx:
                LDA Object_x_hi,x
                CMP temp2
                BEQ +noMonsterToLoadInThisColumn             
                INX             
                CPX #TOTAL_MAX_OBJECTS
                BNE monsterDupeCheckx 
                monsterDupeChecky:
                LDA Object_y_hi,y
                CMP temp2
                BEQ +noMonsterToLoadInThisColumn             
                INX             
                CPX #TOTAL_MAX_OBJECTS
                BNE monsterDupeChecky       
          
                LDA temp
                AND #%11110000
                STA temp1
                CMP #%11110000
                BEQ +noMonsterToLoadInThisColumn
                     CreateObjectOnScreen temp2, temp1, tempD, #$00, scrollUpdateScreen
            +noMonsterToLoadInThisColumn:
    RTS
 

9Panzer

Well-known member
Thank you @AllDarnDavey

This fixed my pickups from duplicating. I'm getting some strange problems though. When I return to the same screen, I seem to get random results. Sometime only 1 pickup spawns, sometimes none, sometimes 2. It seems to depend on how many screens I've done past the objects before returning and the direction I approach them in. It's really odd.
Has anyone been able to crack this issue?

Now that I am trying to use objects as platforms and obstacles its making level design a little more difficult. Objects seem to load whenever the feel like it in my scrolling games which is very annoying. Here is a perfect example, after the player dies you can clearly see some objects just refuse to load. I spoke with CutterCross and Mugi in the discord and they think it might be related to the object ram not clearing properly, but I nothing I was trying really worked.

 

9Panzer

Well-known member
Okay so my hookey solution to this was to make the X value move when the player dies and then destroy the object. So all the objects switch locations so when it does its check for a dupe it doesn't see anything! :)

Just add this to your death routine where-ever you have it hiding!

LDX #TOTAL_MAX_OBJECTS
LDA #0
-
CPX player1_object
BEQ +skipMe

STA Object_x_hi,x

DestroyObject
+skipMe
;; etc
DEX
BPL -
 

TalkingCat

Member
Okay so my hookey solution to this was to make the X value move when the player dies and then destroy the object. So all the objects switch locations so when it does its check for a dupe it doesn't see anything! :)

Just add this to your death routine where-ever you have it hiding!
I tried inserting your code into my death routine and the player object now doesn't go to step 6 of the death animation. Can you tell me what I'm doing wrong?

Video_230912101020.gif

Code:
playerDeath:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

        LDX #TOTAL_MAX_OBJECTS
        LDA #0
        -
        CPX player1_object
        BEQ +skipMe

        STA Object_x_hi,x

        DestroyObject
        +skipMe
        ;; etc
        DEX
        BPL -   
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    StopSound
    PlaySound #sfx_Lose

    ;; STOP the scrolling V2
        LDA scrollByte
        AND #%11111101
        STA scrollByte
        LDA ScreenFlags00
        ORA #%00110000
        STA ScreenFlags00
        
        
        LDA #$00
        STA Object_h_speed_lo,x
        STA Object_h_speed_hi,x
        STA Object_v_speed_lo,x
        STA Object_v_speed_hi,x
        LDA Object_direction,x
        AND #%00000111
        STA Object_direction,x
        
        
         ChangeActionStep player1_object, #$06
  
        Dec myLives
        LDA myLives   
      
      BEQ +livesZero
        JMP +livesNotZero
    +livesZero
    
    
        LDA #%11000010
        LDA #$FE            ;; the screen number you want to warp to
        STA warpToScreen
        LDA #$00                ;; the map to warp to (0:overworld / 1:underworld)
        STA warpMap
        WarpToScreen warpMap, warpToScreen, #$01
        LDA #$FE
        STA gameState
        
        
    +livesNotZero 

    
    RTS
 
Top Bottom