4.5.9 How To Make The Screen Shake: Improved Version

Bucket Mouse

Active member
I've finally souped up my Screen Shake script originally written for 4.1.5. The previous version relied on a subroutine called doWaitFrame to advance the shake to the next frame (without it, the entire computation would happen in one VBlank and you'd see nothing).

But I was informed using doWairFrame wasn't necessary. I was putting the script in doDrawSprites, and doDrawSprites actually runs on every single frame, so it's possible to enter instructions that take multiple frames to display without specifically commanding the NES to do so. Okay, good.

I started using that knowledge to revise the shake script. It was far trickier than I thought, because I had to rethink the entire logic of it. I could no longer do loops within the script itself. Instead I had to rig it so the game would read one part, leave the script, paint the screen, then on its second pass through doDrawSprites, read the next part, and on like that. It required FOUR variables to pull off:

shake1
shake2
shakeCycle
shakeOn

So your first step is to enter those into User Variables. Next you must open doDrawSprites and paste all this at the very end, right after ReturnBank:

Code:
        ;;;;; THIS MATERIAL ADDED TO SHAKE THE SCREEN ON COMMAND
    
    LDA shakeOn
    CMP #$01
    BNE shakeskipper
    
    shake:
    LDA #$04 ; this means it will shake four pixels...change the number to what you desire
    CMP shake1 ; the shake forward variable
    BEQ shakeBack
    INC camY ; move screen ahead by one pixel...change this to camX if you want a horizontal shake
    INC shake1 ; variable counting the current number of pixels away from the norm
    JMP shakeskipper

    shakeBack:
    LDA #$04
    CMP shake2 ; the shake backward variable
    BEQ shakeOver
        DEC camY ;move screen backwards by one pixel...change this to camX if you want a horizontal shake
    INC shake2
    JMP shakeskipper

    shakeOver:
    LDA shakeCycle
    CMP #$04 ; stops shaking after four vibrations...change the number to what you desire
    BEQ resets ; if it's the number, reset all variables...otherwise increase shakeCycle
    INC shakeCycle ; variable for number of vibrations in sequence
    LDA #$00 ; clear cam-counting variables
    STA shake1
    STA shake2
    JMP shakeskipper
    resets: ; resets all variables for next use
    LDA #$00
    STA shake1
    STA shake2
    STA shakeCycle
    STA shakeOn

shakeskipper:

HOW TO USE IT
I've made notes, but just to be clear....

The script as-is will shake the screen vertically by four pixels, four times in a row. You can change the number of shakes and/or the number of pixels by altering the numbers where noted. To shake the script horizontally instead of vertically, change the two mentions of camY to camX. It's fully customizable for your own game. Just remember that the shake forward and the shake back must be the same number.

WHERE TO PUT IT
The shake script turns on and off with a switch. You decide where that switch is. It's controlled by this handy little script:

Code:
    LDA #$01
    STA shakeOn

I use it as a monster AI. It enhances sprite-based cutscenes, and when a boss pounds the ground, you really feel it! Using it as a tile is trickier, since it'll just keep shaking until the sprite gets off. You'll have to write the tile to change on touch. Use this for tiles:

Code:
    TXA
    PHA
    
        ;;;;;;;;; First, let's turn the underlying tile into a normal walkable tile.
    LDY temp1
    LDA temp2
    BEQ +isEvenCt
        ;; is an odd ct, so looking in collisionTable2
        LDA #$00
        STA collisionTable2,y
        JMP +doneWithTileUpdate
    +isEvenCt
        LDA #$00
        STA collisionTable,y
        
    +doneWithTileUpdate

    LDA #$01
    STA shakeOn

    PLA
    TAX

HOW IT'S BETTER THAN MY PREVIOUS VERSION
The shakes are faster. The shake does not pause the action -- sprites will move while the shake is in progress, so you can use it anywhere. Most importantly, EVERYTHING shakes including the sprites, which was not the case in the previous version!
 

9Panzer

Well-known member
Still love the pausing effect in the old one. Makes it scream "THE WORLD IS ENDING!!!" lol
 

offparkway

Active member
Awesome! I just tried to implement this in 4.5.9 but I don't see any results. I pasted the code at the end of doDrawSprites (you said after "ReturnBank", but I don't see ReturnBank anywhere. The script ends with RTS, so I put it after that).

Created the variables and everything, and set it as an enemy AI. The game compiles and runs just fine, but nothing happens. I tried attaching the shakeOn "switch" code to an input button for testing, but couldn't make it do anything there either. Am I missing something?
 

dale_coop

Moderator
Staff member
Maybe you added the code in the wrong script...?

check again your doDrawSprites script... (open the "Project Settings > Script Settings" and under "Subroutines" you should see the doDrawSprites script assigned to the "Handle drawing Sprites", used for your project. Select and click on the "Edit" button. The code needs to be added at the end of that script)
 

offparkway

Active member
Maybe you added the code in the wrong script...?

check again your doDrawSprites script... (open the "Project Settings > Script Settings" and under "Subroutines" you should see the doDrawSprites script assigned to the "Handle drawing Sprites", used for your project. Select and click on the "Edit" button. The code needs to be added at the end of that script)
That’s what I’m editing, yes. Although I’m using the Platform version (in the brawler module). Would that make a difference? Does this script maybe not like scrolling games?

I don’t see “ReturnBank” in the standard doDrawSprites either, though.
 
Last edited:

dale_coop

Moderator
Staff member
oh, maybe not compatible with the scrolling modules? (I haven't tested myself with those).
 

offparkway

Active member
Possibly. I don’t know why it wouldn’t. I’ve tried using it on both scrolling and static screens, but no success.
 

crazygrouptrio

Active member
For anyone struggling to implement this script, I did some messing and got it to work. The guide just lacks a few details.
I don't know where this mysterious "ReturnBank" is, because none of the doDrawSprite scripts, regardless of module, have that anywhere in them. Maybe it was there in a previous build? Anyway, just slap the provided code under doneDrawingThisSprite:. You'll also want to add two lines of code after that so that it looks like this.
Code:
    doneDrawingThisSprite:
        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        LDA shakeOn  ; add these two lines before shake:
        BEQ shakeskipper
If you don't, the screen just shakes incessantly. Those two lines check to see if shake is turned on first before running the shake script.
NOW the screen will shake as intended.
It's also worth noting if you want to change the speed of the shakes, make sure you change the speed after both "shake" and "shakeBack", otherwise your screen will fly away.
 

Moehr

New member
Totally stealing this. Here's a mod to Bucket Mouse's code so it runs on a single variable, if you don't want the level of customization that multiple variables gives you. I used gameTimerLo but you could use whatever you like:


;;;;;;from Bucket Mouse's screenshake script
;;;;;;https://www.nesmakers.com/index.php...creen-shake-improved-version.6867/#post-37974

;;;;; THIS MATERIAL ADDED TO SHAKE THE SCREEN ON COMMAND
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LDA gameTimerLo ; add these two lines before shake:
AND #%10000000 ;new setting for magic effects, bit 80 is for quake
CMP #%00000000
BEQ +shakeSkipper
shake:
LDA gameTimerLo
AND #%00000100 ;shake left and right by 01,10,11, using 100 to determine which way [100 or 000]
CMP #%00000100
BEQ +shakeBack
INC camX ;move screen ahead by one pixel...change this to camY if you want a vertical shake
JMP +shakeReverse

+shakeBack
DEC camX ;move screen backwards by one pixel...change this to camY if you want a vertical shake

+shakeReverse
LDA gameTimerLo
AND #%01000000 ;if we've done 8 cycles of 4 right 4 left, it's time to stop
CMP #%01000000
BEQ +shakeOver
shakeIncrement:
LDA gameTimerLo
AND #%00111111 ;otherwise... increase the counter
STA gameTimerLo
INC gameTimerLo
LDA gameTimerLo
ORA #%10000000
STA gameTimerLo
JMP shakeSkipper

+shakeOver ; resets all variables for next use
LDA #$00
STA gameTimerLo
shakeSkipper: ;done shaking

Note - you will need to set gameTimerLo (or the variable of your choice) to #$80 to trigger this, and it will reset itself to #$00 on termination
 
Last edited:

vanderblade

Active member
Anyway to offload this function to a different bank? My doDrawSprites script is jam packed already with custom draw code.
 

SciNEStist

Well-known member
My Camera shake is a little different and customizable, so I thought I would share it here. it lets you set the camera up and down movement in a pattern

STEP 1
This script uses 1 single user variable "camshake" . go to game settings, user variables, then add the variable with a default value of 0


STEP 2
Next, we use a table to create a pattern for the shake to happen in, and put it into the Game>Extra Tables script

Code:
camYtable:
    .db 0,3,3,0,0,3,3,0,0,2,2,2,0,0,0,2,2,2,0,0,0,2,2,0,0,2,2,0,0,1,1,0,0,1,1,0,0,1,0,1,0,1,0,0 ;  this is an explosion

or

Code:
    camYtable:
    .db 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,3,3,3,3,2,2,2,2,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0;; this is a wave

the number in the table offset the camera by that many pixels. picture it like a seismograph


STEP 3

in Subroutines>Handle Camera script, pop one of these in at the very top:

OPTION 1: as a cool effect or cutscene: put this in if you want to trigger the screen shake to happen once then stop

Code:
LDA camshake
CMP #$00
BEQ +
    LDY camshake
    INC camshake
    CPY #44              ;;;MAX STEPS (how long your table is) ;;;
    BCC Updatecamshake
    LDY #0
    STY camshake
 
Updatecamshake:
    LDA camYtable,y
    STA camY
+

then whenever you want the screen to do the shake, set the variable "screenshake" to 1, and itll shake until it gets to the end, then go back to 0 and wait to be set off again. You can do this anywhere, in the physics script, as a player action step, as a tile collision, etc. with this option, it's best to make sure your table starts with a 0.


OPTION 2: if you just want the shake to keep repeating on the screen, i recommend adding a screen flag

in this example, you use the screen flag 7 and if you spawn into a screen with it turned on, the shake begins immediatly.
you might want to go in to game settings > project labels > screen flags and change the label to something like "shake this screen"

Code:
LDA ScreenFlags00
AND #%00000001        ;;  screen flag 7
BEQ +
    INC camshake
    LDY camshake
    CPY #44              ;;;MAX STEPS ;;;
    BCC Updatecamshake
    LDY #0
    STY camshake
 
Updatecamshake:
    LDA camYtable,y
    STA camY
+

with this option the shake will just cycle untill you spawn elsewhere or the screenflag is turned off.


DONE
all within 15 lines of code, you have a working camera shake to make your enemies more intimidating, or explosions more epic, or just make the player queezy.
 
Last edited:

TheRetroBro

Active member
For anyone struggling to implement this script, I did some messing and got it to work. The guide just lacks a few details.
I don't know where this mysterious "ReturnBank" is, because none of the doDrawSprite scripts, regardless of module, have that anywhere in them. Maybe it was there in a previous build? Anyway, just slap the provided code under doneDrawingThisSprite:. You'll also want to add two lines of code after that so that it looks like this.
Code:
    doneDrawingThisSprite:
        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        LDA shakeOn  ; add these two lines before shake:
        BEQ shakeskipper
If you don't, the screen just shakes incessantly. Those two lines check to see if shake is turned on first before running the shake script.
NOW the screen will shake as intended.
It's also worth noting if you want to change the speed of the shakes, make sure you change the speed after both "shake" and "shakeBack", otherwise your screen will fly away.
added this and got a branch out of range error
 
Top Bottom