Auto Scroll eats player

saturdayxiii

New member
I was trying to fix this in my game, but I've discovered that the same bug is in the simple shooter tutorial. It's game breaking and very easy to fall into, so I think that qualifies it as a bug in the engine rather than just a glitch to be worked around on a per user basis. Unless someone can instruct me otherwise.

Anyway, when auto scrolling: if the screen is scrolling to the right, and the player presses against the left side of the screen, then the player is pushed into an instant death tile, or solid tile, or warp tile(in my game's case), the player is pushed over that tile with no effect, but then becomes stuck on the left side of the screen with its leftmost sprites wrapped around to the other side of the screen.



I've played around with sprite zero positioning, but nothing changes.

I have a similar glitch in my own game where an npc can walk off the edge of a non-scrolling screen (in the scrolling module) then appear half way through the other side of the screen and get stuck, but I just disabled its movement and haven't investigated that further yet.

If anyone has any insights or a fix, please let me know. ty.
 

Raftronaut

Member
I have no insights here unfortunately, but this is problem is pinned to the top of my laundry list of things I would like to correct in my auto scroll game so I would be very grateful to know the solution. I think error may have something to do with reading the player's X coordinates and getting lost somehow. Thanks for posting, I look forward to anyone with insight on this chiming in...

http://nesmakers.com/viewtopic.php?f=60&t=2356&p=14476#p14476
 

saturdayxiii

New member
I'm sure you're right. To refine that notion, I think that it's a combo of the X co-ordinate going into negative numbers, which would normally cause the screen to change, but then the "edge stops player" flag does it's job; stopping the player from moving not only backwards but forwards on the other side of the screen as well.

Edit: I've entered brainstorming mode. I think the observation below is important, but as I continued on I had another observation that I think is a better direction to go. So I changed the font color to unprioritize it. Maybe it will become relevant later if other ideas aren't working.
I think the bigger problem has to do with the way that the tile is checked. If I'm not pressing inputs when the edge of the screen squishes the player against a solid tile, the player dies. If I press an input towards the edge of the screen, this bug happens. I can't seem to activate the bug without a non-null tile being present. So non-null tiles in combo with button inputs are pushing the player back over the edge of the screen. It's odd that it doesn't matter what kind of non-null tile is there, if the bug requires a non-null tile, then you would think that a instant death or warp action would take place before the player is pushed back over the edge of the screen, but that's clearly not the case. So tile interaction is acknowledged through player movement, but the actual code of the tile doesn't activate.

Okay, pardon my rambling and brainstorming, but now I'm remembering my NPC character who gets stuck the same way as this bug just from walking to the edge of non-scrolling screen. So that makes me think that there's a bug in the screen edge detection. My NPC is flagged to reverse direction when he hits the screen edge, but his X co-ordinate has overflowed or become negative, so the the reverse direction doesn't seem to know which way to send the character and thus becomes stuck. So maybe there's a way to prevent x co-ordinate wrap around in the screen edge detection behavior for players and NPCs?

Unfortunately I'm not code savvy enough to pull this off, or to even know which asm file to look in.
 

Mugi

Member
this propably has something to do again with the way how the boundshandlers are broken in the default engine.
Kasumi brought it out from my compo demo entry, that hugging a solid wall while transtitioning a screen will cause you to warp into an incorrect screen, and after doing some analysis with the whole thing,
i ran into the conclusion that the way the boundhandler is "broken" is that it actually ignores boundary collisions if another collision is already happening (in my case, i hugged a wall, so a right side collision made the game ignore the top bound and wrapped the screen.)

i have a fix for this but it's pretty experimental, and only tested against my own game with the very specific scenario so i cant really guarantee that it will fix what's going on here.
i will post that in a bit once i dig out the files involved.
 

Mugi

Member
okay so here's a set of files:

make a backup of your things before messing with this stuff...


GameEngineData\Routines\Basic\ModuleScripts\MainScripts\ScrollingPlatformer\TileCollisions.asm
Code:
HandleTileCollision:
    ;;;;;ASSUMES vulnerability bit 000x0000, if one, skips collision.
    ;;;;;Six collision points. 
    ;;;;;All collision points checked and registered for all objects.
    ;;;; a solid in any of the points will result in negating all others.
    
        ;;; There are 6 ram variables, collisionPoint0 - collisionPoint5.
        ;;; collisionPoint0 = top left
        ;;; collisionPoint1 = top right
        ;;; collisionPoint2 = bottom right
        ;;; collisionPoint3 = bottom left.
        ;;; collisionPoint4 = mid left
        ;;; collisionPoint5 = mid right.
    
    TXA 
    STA currentObject
    TYA
    STA tempy

	LDA npc_collision
	AND #%11111101
	LDA #$00
	STA npc_collision
	LDA navFlag
	AND #%11111110
	STA navFlag
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; EXPLANATION:
;; In this basic module, both non gravity and gravity scripts are loaded.
;; Under most circumstances, a user will likely make use of one or the other,
;; as they each take up a good chunk of data.  But the benefit of including both is that
;; a platforming game with gravity is just a simple bit flip in the screen flags.
    
    
    LDA screenFlags
    AND #%00100000
    BNE useGravity
    JMP dontUseGravity
useGravity:
	LDA Object_vulnerability,x
	AND #%00100000
	BEQ useGravity2
	LDA Object_physics_byte,x
	AND #%00000010
	BNE useGravity2
	JMP dontUseGravity
useGravity2:

;;;;;;===================================================
;;;;;;***************************************************
;;;;;; TILE COLLISION FOR SCROLLING PLATFORMERS:
;;;;;; For scrolling platformers we will use three types of collision detection:
;;;;;;          1. A routine to check under our feet to determine
;;;;;;              whether or not you are standing on a solid object.

                    ;;; This function will check under neath the object's feet
                    ;;; by arg0 pixels.
                    ;;; it will update bit 0 of the Object_physics_byte.
                    ;;; If that bit is 0, it means the place is free.
                    ;;; If that bit is 1, it means the place is solid.
    ;**********************
       CheckUnderObject #$01

;;;;;;
;;;;;;          2. A routine that checks to see if the potential position leaves you
;;;;;;              with your 'bottom' or 'top' in a solid object, in which case it ejects pixel perfect
;;;;;;              to that tile.

                    ;;; This function checks the POTENTIAL vertical position.
                    ;;; If it results in a point in a solid object, it appropriately
                    ;;; ejects and places the object directly against the point
                    ;;; of collision.
    ; **********************
     CheckForVerticalEjection 


;;;;;; Better check for any vertical updates now, or else
;;;;;; a horizontal collision could cancel out the required
;;;;;; bounds update
    CheckPlayerCameraPositionVertical


;;;;;;
;;;;;;          3. A routine which checks horizontal motion to see if the potential position
;;;;;;              sees a solid collision, in which case it can not move.
 
                    ;;; This function checks potential horizontal position.
                    ;;; without factoring in vertical motion.  If it sees a solid,
                    ;;; it skips motion.
    ; ***********************
      CheckForHorizontalCollision
    
;;;; This is the end of scrolling platform physics.  It automatically jumps to the proper place.  
;;;; Otherwise, if there was no collision, we jump to updating the position.
    ;; we only need to update horizontal position
    
  

    CheckPlayerCameraPositionHorizontal
;;;; if it turns out we were outside of the camera bounds, the macro
;;;; has RTSed out of this routine, so the hoziontal position will
;;;; never be updated.
   
     JSR updateHorizontalPosition
   
     JMP DoneWithTileChecksAndUpdatePosition
;;;;;;;;;;;;;==============================================  


dontUseGravity:
;;;;;;===================================================
;;;;;;***************************************************
;;;;;; TILE COLLISION FOR TOP DOWN GAMES: 

;;;; Before we update the position, though, if this is a scrolling game, we need to see if the player
;;;; is at the edge of the camera.  If he is at the edge of the camera, it
;;;; will do a bounds check.
	;CheckPlayerCameraPosition
	   
;;;; if it turns out we were outside of the camera bounds, the macro
;;;; has RTSed out of this routine, so the hoziontal position will
;;;; never be updated.
 
;;;;;   For top down type games, or games that generally have no gravity but have 4 directional movement,
;;;;;   we only need to check potential position and jump to the appropriate label.
  ;  CheckPotentialPosition
   



    
   ; JSR updateHorizontalPosition
   ; JSR updateVerticalPosition
	
;;;; THis is the end of top down scrolling physics.  It automatically jumps to the proper place.
;;;; Otherwise, if there was no collision, we jump to updating the position.

     JMP DoneWithTileChecksAndUpdatePosition
   
   
   

;;; NO POINTS WERE SOLID    
    ;;;; update hold
DoneWithTileChecksAndUpdatePosition:
   ldx currentObject
    RTS
    
HandleSolidCollision:
	LDA screenFlags
	AND #%00000100 ;; is it autoscrolling?
	BEQ + ;; not autoscrolling
	;;; is auto scrolling
	CPX player1_object
	BNE +
	JSR CheckAutoScrollLeftEdge
	RTS 
+ ;; not auto scrolling.

	LDA xPrev
	STA xHold_hi
	LDA yPrev
	STA yHold_hi
    TYA
    STA tempy
    LDA Object_edge_action,x
    LSR
    LSR
    LSR
    LSR
    BEQ doNothingAtSolid
    TAY
    
    LDA AI_ReactionTable_Lo,y
    STA temp16
    LDA AI_ReactionTable_Hi,y
    STA temp16+1
    
    JSR doReactionTrampolineSolid
    JMP pastReactionTrampolineSolid
doReactionTrampolineSolid:
    JMP (temp16) ;;; this now does the action
            ;; and when it hits the RTS in that action,
            ;; it will jump back to the last JSR it saw,
            ;; which was doNewActionTrampoline...which will immediately
            ;; jump to pastDoNewActionTrampoline.
pastReactionTrampolineSolid:
    ;LDA #$00
    ;STA xHold_lo

doNothingAtSolid:
    ;;;;;;;;;;; Do solid reaction type.
    LDY tempy
    LDX currentObject
    RTS
    
    
ejectUp:
    LDA tileY
    and #%00001111;We can only be 0 to 15 pixels in any given wall
    sta temp
 
    lda Object_y_hi,x
    clc;Subtract one plus that so we're a pixel out of the wall rather than one pixel in it.
    SBC temp
    STA Object_y_hi,x
    LDA #$00
    STA yHold_lo
    STA Object_v_speed_hi,x
    STA Object_v_speed_lo,x
    RTS
    
ejectDown:

    lda tileY
    and #%00001111;we can only be 0 to 15 pixels in any given wall
    eor #%00001111;If we're at position 15 in the tile, we only want to eject 0 (+1) so flip the bits
    sec;Will add the extra one to the position so that we're a pixel out of the wall
    adc Object_y_hi,x;rather than one pixel in it.
    sta Object_y_hi,x
    
    LDA #$00
    STA yHold_lo
    STA Object_v_speed_hi,x
    STA Object_v_speed_lo,x
	
	;;; check the top two collision points to see if is it a prize block.
	LDA collisionPoint0
	CMP #COL_INDEX_PRIZE_BLOCK
	BEQ +
	JMP ++ 
+
	LDA Object_x_hi,x
	CLC
	ADC Object_left,x
	STA tileX
	JMP +++
++
	LDA collisionPoint1
	CMP #COL_INDEX_PRIZE_BLOCK
	BEQ +
	JMP ++
+
	LDA Object_x_hi,x
	CLC
	ADC Object_right,x
	STA tileX
+++


	
	LDA gameHandler
	ORA #%00010000
	STA gameHandler
	;;;;; check other head-hit type objects here.
	;; change collision data.
	;; should still have the correct coordinates in tileX and tileY
	ChangeTileAtCollision #COL_INDEX_PRIZE_BLOCK_OFF, #TILE_INDEX_PRIZE_OFF
	
	TXA
	PHA
	LDA Object_x_hi,x
	STA temp
	LDA Object_y_hi,x
	SEC
	SBC #$1A ;; a bit more than the height of it.

	STA temp1
	LDA Object_scroll,x
	STA temp2
	CreateObject temp, temp1, #OBJ_PRIZE, #$00, temp2
	LDA #$00
	SEC
	SBC #$04
	STA Object_v_speed_hi,x
	
	PLA
	TAX
	
++
    RTS
    
    
    

CheckForCollision:

    ;;;; commonly, we won't want to waste cycling 6 times through this for each object when
    ;;;; all points are at no collision.  If by chance we NEED the zero type collision to do something, 
    ;;;; we can comment out the zero type check in this next line.  But most games are going to have
    ;;;; at least one "blank tile" that does nothing, and most games will use zero for this.
	
	;;;; Another conundrum is that a collision could take place in the current or new nametable.
	;;;; if this collision is of type that draws from screen data (for instance, NPC data or warp data), it could
	;;;; be problematic, as all variables handling these things are loaded with the current collision tables data.
	;;;; tempCol ends up being 0 or 1 based on which collision table this should be referencing.
	
    STA temp
    BNE notZeroTypeCollision
    JMP zeroTypeCollision
notZeroTypeCollision:
    LDA #$00
    STA tile_solidity
DoCheckPointsLoop:
    LDA temp
    TAY
    LDA tileTypeBehaviorLo,y
    STA temp16
    LDA tileTypeBehaviorHi,y
    STA temp16+1
    JSR UseTileTypeTrampoline
    JMP pastTileTypeTrampoline
UseTileTypeTrampoline:
    JMP (temp16)
pastTileTypeTrampoline:
DontCheckTileType0:

    
zeroTypeCollision:
    ldx currentObject
    RTS
    
    
    
    
DetermineCollisionTable:
    LDA tempCol
    BNE colPointInDifferentTable
    ;;;; the collision point is in the current collision table.
    LDA Object_scroll,x
    AND #%00000001
    BNE isInOddTable
    JMP isInEvenTable
colPointInDifferentTable:
    ;;; the collision point to be checked is in the NEXT collision table.
    LDA Object_scroll,x
    AND #%00000001
    BNE isInEvenTable
    JMP isInOddTable
	

isInEvenTable:
    LDA collisionTable,y
    RTS
isInOddTable:
    LDA collisionTable2,y

    RTS


    
    
updateHorizontalPosition:
    LDA xHold_lo
    STA Object_x_lo,x
    LDA xHold_hi
    STA Object_x_hi,x
    LDA nt_hold
	CMP Object_scroll,x
	BEQ justUpdateScroll
	LDA #$01
	STA update_screen_data_flag
	LDA nt_hold
justUpdateScroll:
    STA Object_scroll,x
    
    RTS
    
updateVerticalPosition:
    LDA yHold_lo
    STA Object_y_lo,x
    LDA yHold_hi
    STA Object_y_hi,x   
    RTS


now go to your GameEngineData\Routines\Basic\System\Macros folder and inside that folder, find a file called CheckPlayerCameraPosition.asm

delete that macro (just delete the file. after making a backup elsewhere than in that folder(macro folder autoloads all assembly files from the folder regardless of filename so making a backup by renaming files is not working))

now that you've deleted it, make 2 new files into that folder:

CheckPlayerCameraPositionHorizontal.asm
Code:
MACRO CheckPlayerCameraPositionHorizontal
	CPX player1_object
    BEQ notdoneWithCameraCheckHorizontal
	JMP doneWithCameraCheckHorizontal
notdoneWithCameraCheckHorizontal:
	LDA gameHandler
	AND #%10000000
	BNE gameIsActive
	JMP doneWithCameraCheckHorizontal
gameIsActive:
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;; let's check to see if we're against the leftmost edge.
	LDA xScroll_hi
	BNE + ; not on screen 0
	;; is on screen zero
	LDA Object_h_speed_lo,x
	CLC
	ADC #$00
	LDA Object_h_speed_hi,x
	ADC #$00
	BPL + ;; not moving left.
	LDA Object_scroll,x
	BNE +
	LDA xScroll
	CMP xHold_hi
	BCS leftOfCameraForPlayer
	JMP checkRightEdge
+
    LDA nt_hold
    CMP xScroll_hi
    BCC leftOfCameraForPlayer
	;BEQ leftOfCameraForPlayer
    BNE checkRightEdge
    LDA xHold_hi
    CMP xScroll
    BCC leftOfCameraForPlayer
	;BEQ leftOfCameraForPlayer
    JMP checkRightEdge
leftOfCameraForPlayer:
    ;;; dont update position
    JMP doScrollingLeftBounds
checkRightEdge:
    LDA xScroll_hi
    CLC
    ADC #$01
    STA temp
    
    LDA xScroll
    SEC
    SBC Object_right,x
    STA temp1
    LDA temp
    SBC #$00
    STA temp

    CMP nt_hold
    BCC rightOfCameraEdgeForPlayer
	;BEQ rightOfCameraEdgeForPlayer
    BNE doneWithCameraCheckHorizontal
    LDA temp1
    CMP xHold_hi
    BCC rightOfCameraEdgeForPlayer
	;BEQ rightOfCameraEdgeForPlayer
    JMP doneWithCameraCheckHorizontal
rightOfCameraEdgeForPlayer:
    JMP doScrollingRightBounds
  
doScrollingLeftBounds:
    ;;; enter what should happen at bounds for player.
	;;; an RTS here will result in skipping position update.
	JSR doLeftBounds_player
    RTS    
    
doScrollingRightBounds:
    ;;; enter what should happen at bounds for player.
	;;; an RTS here will result in skipping position update.
	JSR doRightBounds_player
    RTS

doneWithCameraCheckHorizontal:
    ENDM


CheckPlayerCameraPositionVertical.asm
Code:
MACRO CheckPlayerCameraPositionVertical
	CPX player1_object
    BEQ notdoneWithVerticalCameraCheck
	JMP doneWithVerticalCameraCheck
notdoneWithVerticalCameraCheck:
	LDA gameHandler
	AND #%10000000
	BNE gameIsActiveCheckVertical
	JMP doneWithVerticalCameraCheck
gameIsActiveCheckVertical:

;;;;; TOP CHECK
	LDA Object_y_hi,x
	CLC
	ADC #$7F
	STA temp
	LDA Object_y_lo,x
	CLC
	ADC Object_v_speed_lo,x
	LDA temp
	ADC Object_v_speed_hi,x
	BVC notAtBoundsTop
	LDA #$00
	STA xScroll
	JSR doTopBounds_player
	RTS
;;;;; BOTTOM CHECK
notAtBoundsTop:
	LDA Object_y_lo,x
	CLC
	ADC Object_v_speed_lo,x
	LDA Object_y_hi,x
	ADC Object_v_speed_hi,x
	ADC Object_bottom,x
	CMP #BOUNDS_BOTTOM
	BCS atBottomBounds
	JMP doneWithVerticalCameraCheck
atBottomBounds:
	LDA #$00
	STA xScroll
	JSR doBottomBounds_player
	RTS

doneWithVerticalCameraCheck:
    ENDM


now compile the game and see if anything improved.

again, this fixes a few issues regarding the edge collisions, but this was tailored specifically to combat known issues in my game and has not been tested further than that, so it might or might not help.
 

saturdayxiii

New member
Trying it now. in the 4.1.5 Simple Shooter module.

I'm guessing that I should put the tilecollision code in ModuleScripts/MainScripts/TileCollision_4_1_0.asm as that's where I got a "label already used (112)" error when I first ran this script. Copy paste there and progress seems to ensue:

Now the error is:
Routines\Basic\ModuleScripts\MainScripts\TileCollision_4_1_0.asm(258):ChangeTileAtCollision(18): Unknown label.
Routines\Basic\ModuleScripts\MainScripts\TileCollision_4_1_0.asm(258):ChangeTileAtCollision(23): Unknown label.

The lines pointed to in the ChangeTileAtCollision macro says "BNE +" and "+" respectively. So I don't know what the problem is there. Here's the whole macro for ease of access:
Code:
MACRO ChangeTileAtCollision arg0, arg1
	;; this changes a tile at the current point of collision.
	;; collision is determined by what is currently loaded into the
	;; tileX and tileY variables. 
	
	;; arg0 - new collision type
	;; arg1 - starting tile number of new metatile.
	LDA #$01
	STA tileCollisionFlag

	
	JSR GetTileAtPosition
;	TAY
	;LDA arg0
	;STA temp
	LDA Object_scroll,x
	AND #%00000001
	BNE +
	LDA arg0
	STA collisionTable,y
	JMP ++
	
+
	LDA arg0
	STA collisionTable2,y

++
	
	;;;;; this will change the graphics
	JSR ConvertCollisionToNT
	;l0dA #$20
	;STA temp16
	;LDA Object_x_hi,x
	;AND #%11110000
	;CLC
	;ADC #$f0
	;STA temp16+1
	LDA temp16
	STA tempTileUpdate_lo
	LDA temp16+1
	STA tempTileUpdate_hi
	

	LDA arg1
	STA updateTile_00
	STA tempChangeTiles
	LDA arg1
	CLC
	ADC #$01
	STA updateTile_01
	STA tempChangeTiles+1
	LDA arg1
	CLC
	ADC #$10
	STA updateTile_02
	STA tempChangeTiles+2
	LDA arg1
	CLC
	ADC #$11
	STA updateTile_03
	STA tempChangeTiles+3

	;;TXA
	;PHA
	;JSR HandleUpdateNametable
	;PLA 
	;TAX
	LDA #$01
	STA testFlagThing
	
	ENDM

Also, it appears that your edit to the TileCollision.asm removed position checks and updates for no-gravity scrolling, which I assume the shooter module rather depends on.
 

Mugi

Member
Thats a new one, no idea why it would suddenly start complaining about a completely unrelated macro and about a label that doesnt really seem to have anything wrong with it either.

That said, the tilecollisions is from the 4.1.4 platformer module so maybe it differs from the shooter one, i will take a look when i get to my pc.

In the platformer module its inside the platformer folder, but essentially you want to use this instead of whatever it is that you have loaded into your game (see script settings -----> handle tile collisions)
 

saturdayxiii

New member
Here's an interesting development. There exists a macro called CheckPotentialPositionScroll, also one called CheckPotentialPositionSafe but it seemed to act the same. Before applying Mugi's supplied code, I go into ModuleScripts/MainScripts/TileCollision_4_1_0.asm and change line 120 "CheckPotentialPosition" to "CheckPotentialPositionScroll", then the ship no longer gets stuck in the wall. It still passes through the instant death tiles, and even appears to get stuck in the screen edge, but I can move out and continue playing without breaking the game completely. That's an improvement IMO.
 

Mugi

Member
Thats goog to hear. I will still take a look at whats going on with the shooter tilecollisions though.

My code doesnt actually modify it, but changes the way the cameraposition macros function instead so it should be importable to the shooter module all the same.

Anyway, ill let you know when i have something more than a phone to work with xD
 

saturdayxiii

New member
Okay, Now I'm editing my game in the adventure module, which is actually using the module based asm file. For anyone following this thread in the future, it's best to go through the script menu in project settings. It's called HandleTileCollision.asm and the CheckPotentialPosition is on line 45.

This time CheckPotentialPositionScroll had no difference on the bug, while CheckPotentialPositionSafe prevented the game from completely crashing after passing through solid tiles.
 

Raftronaut

Member
saturdayxiii said:
Here's an interesting development. There exists a macro called CheckPotentialPositionScroll, also one called CheckPotentialPositionSafe but it seemed to act the same. Before applying Mugi's supplied code, I go into ModuleScripts/MainScripts/TileCollision_4_1_0.asm and change line 120 "CheckPotentialPosition" to "CheckPotentialPositionScroll", then the ship no longer gets stuck in the wall. It still passes through the instant death tiles, and even appears to get stuck in the screen edge, but I can move out and continue playing without breaking the game completely. That's an improvement IMO.

WOW, this fix dramatically improved my game demo. It's true, pressing an input while colliding with any tile other than NULL value causes the bug, collisions do not register until the character moves away from the screen edge. Although, Monster collisions still work when the character is in the screen edge. So maybe the solution lies somewhere in the differences between Monster Collisions vs. Tile collisions?

Thanks for the assistance on this issue, I posted this same topic about one month ago during the competition but no one replied. I suspect most people avoided the shooter module for the byte off. I'm really excited to see where this is going!
 

saturdayxiii

New member
Great idea Raftronaut. I'm tied up with other things today, but I'll definitely start hunting for Monster collision code when I have a chance.
 

Mugi

Member
i just checked this out and the tilecollisions.asm used by scrolling platformer is literally the same as the one used by the scrolling shooter (im going by 4.1.4 here though, but afaik, it should be the same in 4.1.5)

anyway, since im not even sure if i made modifications to it further than what this needs, i wrote it clean based on the scrolling shooter module's vanilla file.

same thing applies, delete the macro i mentioned, and make the 2 new macro files in the macro folder.
then assign this to your tilecollisions.asm in project settings -> script settings.

it should fix the whole deal of solid collisions cancelling out boundary checks, which im suspecting is the root cause of the bug you guys have (although i havent tried making shooters, it uses the same scroll engine than platformers.)


scrollingshooter tilecollisions.asm

Code:
HandleTileCollision:
    ;;;;;ASSUMES vulnerability bit 000x0000, if one, skips collision.
    ;;;;;Six collision points. 
    ;;;;;All collision points checked and registered for all objects.
    ;;;; a solid in any of the points will result in negating all others.
    
        ;;; There are 6 ram variables, collisionPoint0 - collisionPoint5.
        ;;; collisionPoint0 = top left
        ;;; collisionPoint1 = top right
        ;;; collisionPoint2 = bottom right
        ;;; collisionPoint3 = bottom left.
        ;;; collisionPoint4 = mid left
        ;;; collisionPoint5 = mid right.
    
    TXA 
    STA currentObject
    TYA
    STA tempy

	LDA npc_collision
	AND #%11111101
	LDA #$00
	STA npc_collision
	LDA navFlag
	AND #%11111110
	STA navFlag
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; EXPLANATION:
;; In this basic module, both non gravity and gravity scripts are loaded.
;; Under most circumstances, a user will likely make use of one or the other,
;; as they each take up a good chunk of data.  But the benefit of including both is that
;; a platforming game with gravity is just a simple bit flip in the screen flags.
    
    
    LDA screenFlags
    AND #%00100000
    BNE useGravity
    JMP dontUseGravity
useGravity:
	LDA Object_vulnerability,x
	AND #%00100000
	BEQ useGravity2
	LDA Object_physics_byte,x
	AND #%00000010
	BNE useGravity2
	JMP dontUseGravity
useGravity2:

;;;;;;===================================================
;;;;;;***************************************************
;;;;;; TILE COLLISION FOR SCROLLING PLATFORMERS:
;;;;;; For scrolling platformers we will use three types of collision detection:
;;;;;;          1. A routine to check under our feet to determine
;;;;;;              whether or not you are standing on a solid object.

                    ;;; This function will check under neath the object's feet
                    ;;; by arg0 pixels.
                    ;;; it will update bit 0 of the Object_physics_byte.
                    ;;; If that bit is 0, it means the place is free.
                    ;;; If that bit is 1, it means the place is solid.
    ;**********************
       CheckUnderObject #$01

;;;;;;
;;;;;;          2. A routine that checks to see if the potential position leaves you
;;;;;;              with your 'bottom' or 'top' in a solid object, in which case it ejects pixel perfect
;;;;;;              to that tile.

                    ;;; This function checks the POTENTIAL vertical position.
                    ;;; If it results in a point in a solid object, it appropriately
                    ;;; ejects and places the object directly against the point
                    ;;; of collision.
    ; **********************
     CheckForVerticalEjection 

  
;;;;;; Better check for any vertical updates now, or else
;;;;;; a horizontal collision could cancel out the required
;;;;;; bounds update
    CheckPlayerCameraPositionVertical


;;;;;;
;;;;;;          3. A routine which checks horizontal motion to see if the potential position
;;;;;;              sees a solid collision, in which case it can not move.
 
                    ;;; This function checks potential horizontal position.
                    ;;; without factoring in vertical motion.  If it sees a solid,
                    ;;; it skips motion.
    ; ***********************
      CheckForHorizontalCollision
    
;;;; This is the end of scrolling platform physics.  It automatically jumps to the proper place.  
;;;; Otherwise, if there was no collision, we jump to updating the position.
    ;; we only need to update horizontal position
    
  

    CheckPlayerCameraPositionHorizontal
;;;; if it turns out we were outside of the camera bounds, the macro
;;;; has RTSed out of this routine, so the hoziontal position will
;;;; never be updated.
   
     JSR updateHorizontalPosition
   
     JMP DoneWithTileChecksAndUpdatePosition
;;;;;;;;;;;;;==============================================  


dontUseGravity:
;;;;;;===================================================
;;;;;;***************************************************
;;;;;; TILE COLLISION FOR TOP DOWN GAMES: 

;;;; Before we update the position, though, if this is a scrolling game, we need to see if the player
;;;; is at the edge of the camera.  If he is at the edge of the camera, it
;;;; will do a bounds check.
	;CheckPlayerCameraPosition
	   
;;;; if it turns out we were outside of the camera bounds, the macro
;;;; has RTSed out of this routine, so the hoziontal position will
;;;; never be updated.
 
;;;;;   For top down type games, or games that generally have no gravity but have 4 directional movement,
;;;;;   we only need to check potential position and jump to the appropriate label.
  ;  CheckPotentialPosition
   



    
   ; JSR updateHorizontalPosition
   ; JSR updateVerticalPosition
	
;;;; THis is the end of top down scrolling physics.  It automatically jumps to the proper place.
;;;; Otherwise, if there was no collision, we jump to updating the position.

     JMP DoneWithTileChecksAndUpdatePosition
   
   
   

;;; NO POINTS WERE SOLID    
    ;;;; update hold
DoneWithTileChecksAndUpdatePosition:
   ldx currentObject
    RTS
    
HandleSolidCollision:
	LDA screenFlags
	AND #%00000100 ;; is it autoscrolling?
	BEQ + ;; not autoscrolling
	;;; is auto scrolling
	CPX player1_object
	BNE +
	JSR CheckAutoScrollLeftEdge
	RTS 
+ ;; not auto scrolling.

	LDA xPrev
	STA xHold_hi
	LDA yPrev
	STA yHold_hi
    TYA
    STA tempy
    LDA Object_edge_action,x
    LSR
    LSR
    LSR
    LSR
    BEQ doNothingAtSolid
    TAY
    
    LDA AI_ReactionTable_Lo,y
    STA temp16
    LDA AI_ReactionTable_Hi,y
    STA temp16+1
    
    JSR doReactionTrampolineSolid
    JMP pastReactionTrampolineSolid
doReactionTrampolineSolid:
    JMP (temp16) ;;; this now does the action
            ;; and when it hits the RTS in that action,
            ;; it will jump back to the last JSR it saw,
            ;; which was doNewActionTrampoline...which will immediately
            ;; jump to pastDoNewActionTrampoline.
pastReactionTrampolineSolid:
    ;LDA #$00
    ;STA xHold_lo

doNothingAtSolid:
    ;;;;;;;;;;; Do solid reaction type.
    LDY tempy
    LDX currentObject
    RTS
    
    
ejectUp:
    LDA tileY
    and #%00001111;We can only be 0 to 15 pixels in any given wall
    sta temp
 
    lda Object_y_hi,x
    clc;Subtract one plus that so we're a pixel out of the wall rather than one pixel in it.
    SBC temp
    STA Object_y_hi,x
    LDA #$00
    STA yHold_lo
    STA Object_v_speed_hi,x
    STA Object_v_speed_lo,x
    RTS
    
ejectDown:

    lda tileY
    and #%00001111;we can only be 0 to 15 pixels in any given wall
    eor #%00001111;If we're at position 15 in the tile, we only want to eject 0 (+1) so flip the bits
    sec;Will add the extra one to the position so that we're a pixel out of the wall
    adc Object_y_hi,x;rather than one pixel in it.
    sta Object_y_hi,x
    
    LDA #$00
    STA yHold_lo
    STA Object_v_speed_hi,x
    STA Object_v_speed_lo,x
	
	;;; check the top two collision points to see if is it a prize block.
	LDA collisionPoint0
	CMP #COL_INDEX_PRIZE_BLOCK
	BEQ +
	JMP ++ 
+
	LDA Object_x_hi,x
	CLC
	ADC Object_left,x
	STA tileX
	JMP +++
++
	LDA collisionPoint1
	CMP #COL_INDEX_PRIZE_BLOCK
	BEQ +
	JMP ++
+
	LDA Object_x_hi,x
	CLC
	ADC Object_right,x
	STA tileX
+++


	
	LDA gameHandler
	ORA #%00010000
	STA gameHandler
	;;;;; check other head-hit type objects here.
	;; change collision data.
	;; should still have the correct coordinates in tileX and tileY
	ChangeTileAtCollision #$00, #TILE_INDEX_PRIZE_OFF
	
	TXA
	PHA
	LDA Object_x_hi,x
	STA temp
	LDA Object_y_hi,x
	SEC
	SBC #$12 ;; a bit more than the height of it.
	STA temp1
	LDA Object_scroll,x
	STA temp2
	CreateObject temp, temp1, #OBJ_PRIZE, #$00, temp2
	LDA #$00
	SEC
	SBC #$04
	STA Object_v_speed_hi,x
	
	PLA
	TAX
	
++
    RTS
    
    
    

CheckForCollision:

    ;;;; commonly, we won't want to waste cycling 6 times through this for each object when
    ;;;; all points are at no collision.  If by chance we NEED the zero type collision to do something, 
    ;;;; we can comment out the zero type check in this next line.  But most games are going to have
    ;;;; at least one "blank tile" that does nothing, and most games will use zero for this.
	
	;;;; Another conundrum is that a collision could take place in the current or new nametable.
	;;;; if this collision is of type that draws from screen data (for instance, NPC data or warp data), it could
	;;;; be problematic, as all variables handling these things are loaded with the current collision tables data.
	;;;; tempCol ends up being 0 or 1 based on which collision table this should be referencing.
	
    STA temp
    BNE notZeroTypeCollision
    JMP zeroTypeCollision
notZeroTypeCollision:
    LDA #$00
    STA tile_solidity
DoCheckPointsLoop:
    LDA temp
    TAY
    LDA tileTypeBehaviorLo,y
    STA temp16
    LDA tileTypeBehaviorHi,y
    STA temp16+1
    JSR UseTileTypeTrampoline
    JMP pastTileTypeTrampoline
UseTileTypeTrampoline:
    JMP (temp16)
pastTileTypeTrampoline:
DontCheckTileType0:

    
zeroTypeCollision:
    ldx currentObject
    RTS
    
    
    
    
DetermineCollisionTable:
    LDA tempCol
    BNE colPointInDifferentTable
    ;;;; the collision point is in the current collision table.
    LDA Object_scroll,x
    AND #%00000001
    BNE isInOddTable
    JMP isInEvenTable
colPointInDifferentTable:
    ;;; the collision point to be checked is in the NEXT collision table.
    LDA Object_scroll,x
    AND #%00000001
    BNE isInEvenTable
    JMP isInOddTable
	

isInEvenTable:
    LDA collisionTable,y
    RTS
isInOddTable:
    LDA collisionTable2,y

    RTS


    
    
updateHorizontalPosition:
    LDA xHold_lo
    STA Object_x_lo,x
    LDA xHold_hi
    STA Object_x_hi,x
    LDA nt_hold
	CMP Object_scroll,x
	BEQ justUpdateScroll
	LDA #$01
	STA update_screen_data_flag
	LDA nt_hold
justUpdateScroll:
    STA Object_scroll,x
    
    RTS
    
updateVerticalPosition:
    LDA yHold_lo
    STA Object_y_lo,x
    LDA yHold_hi
    STA Object_y_hi,x   
    RTS
 

Raftronaut

Member
Actually,

Upon further testing, I discovered the game breaking glitch can still be triggered even with this fix in place. Although, I haven't quite determined the circumstances needed to trigger it. I was able to determine one sure way to make it happen, pressing down on the solid tiles at the bottom of the screen while hugging the screen edge causes the player to wrap into the screen edge like before, and also like before, the player cannot escape. while stuck here the player cannot change the Y axis position.

Side note: pressing against the top or bottom of a solid tile seems to work like a hard stop on the player input. At least if you nudge the ceiling or the ground the character speed decreases or stops. The collision of which is maybe contributing to the screen wrap. Wondering if there is a way to make a "roller" tile. A solid tile that will not stop player momentum when touched, like a platform conveyor belt, but one that simply allows momentum to not be changed upon collision....that might completely negate the ability for this glitch to happen when close to the screen top or bottom .....just brainstorming here...
 

Raftronaut

Member
ooooh,

Mugi, I am going to test your script today and see what happens.

I hope to report back good news, fingers crossed!
 

Raftronaut

Member
oh no!

This broke my game. Character is stuck in place with no input control. and projectiles has zero movement as well .they just trail off behind the player. I had to replace Tile collisions script and reinstate the CheckPlayerCameraPosition.asm macro and reload my project in order to get inputs working again.

not quite sure what I did wrong yet. I may back up my whole project and script folders and try one more time
 

Mugi

Member
does the shooter module actually load the tilecollisions from the scrollingshooter folder or does it use the generic one by default.

there are no changes made to the tilecollisionsasm itself so it shouldn't break things.
the macros are just separated for vertical and horizontal for the ease of use and only thing this modification really does is makes the game check the boundaries instead of skipping them when a collision does happen during contact with a boundary.

aside that, the only thing i can think off from the top of my head is that there are version differences that im not aware of since im still stuck at 4.1.4
 
Top Bottom