Parallax Examples?

jakenastysnake

New member
Love the effect and think it gives some good depth to screens. Would anyone with some basic examples of how they pulled off the technique be willing to share? I would greatly appreciate the knowledge!
 
Parallax is really difficult effect to pull off on the NES, because you do not really have different 'layers' for your background tiles.

The only person I have seen make a similar effect/may be able to help is Kasumi. But it's likely going to involve some heavy custom scripting.
 

Mugi

Member
mapper 30 makes parallaxing slightly complicated since the mapper doesnt have scanline counter, which basically gives one easy option out.
you can use sprite0 to do a screensplit for a single parallaxing effect on screen but that also means that you will lose the ability to use the tile hud, as the parallax would reserve the sprite0 for it's own purpose (sprite0 is only usable once per frame)

i considered doing this since im not using the tile hud so my game currently doesnt use sprite0 for anything, but it's not really high on the priority list at the moment.
i will make a write up about that assuming i actually do it (would be neat for parallaxing a title screen logo or something simple like that.)
 

jakenastysnake

New member
chronicleroflegends said:
Parallax is really difficult effect to pull off on the NES, because you do not really have different 'layers' for your background tiles.

The only person I have seen make a similar effect/may be able to help is Kasumi. But it's likely going to involve some heavy custom scripting.

Hmm, I see. I'm still learning the ins and outs of ASM scripting so i should probably put that on the back burner until I get better.

By the way, I checked out your game Nix: The Paradox Relic. Awesome job man! I've mainly been watching the NESMaker tutorials. Are there any other tutorials/resources you'd recommend?
 

jakenastysnake

New member
Mugi said:
mapper 30 makes parallaxing slightly complicated since the mapper doesnt have scanline counter, which basically gives one easy option out.
you can use sprite0 to do a screensplit for a single parallaxing effect on screen but that also means that you will lose the ability to use the tile hud, as the parallax would reserve the sprite0 for it's own purpose (sprite0 is only usable once per frame)

i considered doing this since im not using the tile hud so my game currently doesnt use sprite0 for anything, but it's not really high on the priority list at the moment.
i will make a write up about that assuming i actually do it (would be neat for parallaxing a title screen logo or something simple like that.)

I have a basic hud using the sprite0 in my current project. If you do happen to do anything with parallaxing in the future I'd love to see what you make. A write up would be great for beginners like me!
 

Kasumi

New member
This got long...

The screen renders from left to right, top to bottom. While the screen is rendering (done by NES' picture processing unit, or PPU), the CPU is running separately. The CPU can talk to the PPU. If the CPU provides no new information to the PPU, the PPU will render a 256x240 region starting from the coordinate that was last given to it by the CPU.

The NES has four 256x240 screens in memory. It's easiest to describe them as laid out in a 2x2 rectangle. Usually two are identical to each other. (Either the left 2 screens are identical and the right 2 screens are identical, or the top two screens are identical and the bottom two screens are identical.)

If the PPU ever gets to the end of the 2x2 rectangle while rendering, it will wrap back to the beginning. (Say it reaches the right edge, but still has more pixels to draw. It will grab the rest from the left edge.) Another way to think about it is that that 2x2 rectangle repeats infinitely in all directions.

Visual time. Here is what the screens in memory look like when the left 2 screens are the same and the right 2 screens are the same:
PVvnJXi.gif

Here is what the screens in memory look like when the top 2 screens are the same and the bottom two screens are the same:
kGjJhMr.gif


To break down the above: You've got a 512x480 image that repeats endlessly in all directions. You can tell the PPU to start drawing at any point within that infinitely repeating canvas, and it will draw the 256x240 pixels for the screen starting at that point.

But here's where things get interesting (and dense)! You can specify a new point while it's still in the middle of drawing the screen. Super Mario Bros. and NES Maker both do this for their HUDs. They say, "PPU, start drawing from the top left corner of the 2x2 rectangle". But once all the horizontal lines of the HUD have been drawn, they say, "PPU, start drawing from the pixel immediately below the HUD in the 2x2 rectangle, and X pixels into the 2x2 rectangle." (Where X is the scroll position.)

And here's where things get more interesting. There's not really a limit to how many times you can tell the PPU to start drawing from a different place. (Well, there are rules to it, but lets ignore that for now.) So here's Rad Racer:

LZYCmCT.gif

Notice the four screens don't really change as the road changes shape. All that's happening is the CPU telling the PPU to keep drawing from different parts of the road multiple times throughout the entire screen.

And that's essentially what a parallax does (or at least the main way they're done). "For rows 0-32 draw at 0, 0. For rows 33-64 draw at 16, 33. For rows 65-96 draw at 24, 65". It just sets the X positions to draw from so they move at different speeds. (one pixel of movement in one section is two pixels of movement in another)

There's even a caveat the comes with that. See this tweet: https://twitter.com/bitinkstudios/status/1057486918693388288
You can't have something in a row not move while the rest of the row does move (or at least the way main this is done). This is because of how the PPU renders (left to right, top to bottom). If you want the top half of the screen to be different from the bottom of the screen, it's one change in the middle that will affect the rest. To make the left half the screen different from the right half of the screen, you have to make two changes on every single row. And since the CPU is running while the PPU is, this basically means 100% of the CPU would be used doing only this. (There are probably other reasons why it's not possible or feasible.)

But here's the issue with doing this. The CPU is running while the PPU is. To make the change at say... row 32, you have to pass the new starting position EXACTLY then. And the PPU can't tell the CPU it's at row 32. So the CPU has to keep track of how much time has passed between when the PPU started at row zero (which the PPU does tell the CPU about) and row 32.

While the CPU is counting time, it can't do much else as far as game logic. (Game logic makes decisions, different decisions take different amounts of time, and we need to give the message to the PPU at an exact time.)

There's one saving grace. That's Sprite Zero. The PPU tells the CPU when it starts at row zero. And the PPU can notify the CPU when a specific sprite has an opaque pixel overlap with an opaque pixel background. The CPU can wait for this notification to give the PPU ONE new point on any given frame. (Even then, the CPU can't really do much else while waiting...)

Suppose you want to make a change in the middle of the screen. You are using HALF THE AVAILABLE CPU TIME PER FRAME just waiting. Half of time you could be spending on calculating object collisions, half the time you could be spending on lots of things.

Some cartridges allow the cartridge to notify the CPU when the PPU starts on certain rows. (And in a way that there's no time spent on the actual waiting.) NES Maker's cartridge cannot do this. So it can't do more than one split without extreme, time wasting code. (Interestingly, Rad Racer's cartridge also cannot do that. It is entirely extreme code.)

There are two other ways to do parallax effects, but both require reasonably strong understanding of how to talk to the PPU. Maybe for some other post... this got long.

Edit: Or I guess the basic example is already in NES Maker's code. GameEngineData/Routines/Basic/MainASM.asm. But it's only basic due to the prerequisite knowledge of the rest of this post. ;)

ctrl+F WaitSprite0. It loops until the PPU reports the pixels are overlapping. (a bit gets set in $2002 when this is true. This is also why the game freezes when sprite 0 isn't set up properly. If pixels will never overlap, the CPU will never leave this loop.) The writes to $2005 are the X and Y position for where to start drawing from. To do a really basic parallax (instead of a HUD), what you'd do is write a different X position to $2005 for the beginning of the screen (as opposed to always zero like the HUD, "; start with no scroll for status bar"), and this HUD code would set the scroll properly for the rest of the screen assuming you set sprite 0 correctly.

All bets are off once you want to do more than one split. That's the wild west.
 

jakenastysnake

New member
@kasumi

Dude.. Kudos for the explanation and visuals. Definitely gives me something to bite into and play around with. Thanks a ton!

So glad I found this forum lol
 

Kasumi

New member
An addendum/correction. The rectangle that infinitely repeats is 512x480, but only single bytes are written to $2005. The writes to $2005 (usually) choose where within a single screen. To choose which of the four screens, is a write to $2000. (The lowest bits choose which of the four screens: http://wiki.nesdev.com/w/index.php/PPU_registers#Controller_.28.242000.29_.3E_write )

There are other complications, though. Giving a new X position to the PPU is relative easy. Giving a new Y position to the PPU is pretty complicated: https://wiki.nesdev.com/w/index.php/PPU_scrolling#Split_X.2FY_scroll

I was incorrect when saying the second write to $2005 is the Y position. It normally would be, but midscreen, the wiki claims it doesn't have the effect it normally does. (I haven't tested this, I've only done the crazy way in the link above.)

There's actually a lot to consider, though. Scrolling writes new tiles which could overwrite that you're trying to parallax.
wV96zki.gif

(In addition to telling the PPU to start at a new place, you can actually tell it other things. This is how some games can display more than 256 tiles like Smash TV's title screen or display a textbox without the font appearing in the level graphics.)
The other ways to parallax are:
1. bank swapping (which is probably not too good an option for NES Maker, since only four frames are available for it. Your parallax could only be four unique scroll positions). Metal Storm does it this way, and that's also how it has parallax that has still things and moving things on the same row. (But it dedicates a very significant amount of its total graphics space to the effect. You need a page of tiles in every unique pixel position for all things that will move.) This method is super easy to do, and takes effectively no CPU time, though.
2. Writing unique tiles. This is NES Maker viable, and can similarly allow things on the same row to scroll at different speeds. Battletoads uses it for a vertical parallax on this level:
https://youtu.be/wPd9TzBFc40
Note that the sides of the hole are scrolling at a different speed than the center. This is done by writing each tile index that's meant to move with an identical tile offset by 1 pixel. The screen scrolls normally, but the tiles displayed are offset, creating the effect.

But doing 2 within NES Maker means knowing very, very deeply about the maximum amount of time its other PPU updates will take. Writing tiles takes a lot of time, and there is a very small amount of time where you're able to do it safely every frame. It's reasonably hard to do most types of parallax without a pretty good understanding of the PPU.

Well, the bank switching method is simple, but requires bank switching knowledge.
 

Dirk

Member
I just discovered this thread. Wow, thank you Kasumi for your great explanations and for accompanying them with great illustrations! Always a pleasure to read your postings.
 

Kasumi

New member
For a couple more notes. It's actually not terribly hard to change NES Maker from Mapper 30 to MMC3 (which does have a scanline counter). (For anyone not using mapper 30 features it doesn't support. Which is primarily CHR RAM bank switching, or saves larger than 8KB, neither of which NES Maker uses without you adding them yourself anyway, at this point in time.) That is my own "Maybe I'll write a guide".

Finally, you can use an audio IRQ as a makeshift scanline counter: http://wiki.nesdev.com/w/index.php/APU_DMC#Usage_of_DMC_for_syncing_to_video

But I believe it's easier to switch to MMC3 than do that (with the main caveat that your game can no longer be flashed to a mapper 30 cart). MMC3 does have CHR ROM bank switching, but modifying the engine to use that instead would likely be involved.

But switching to MMC3 alone does nothing. You'd still have to write the code to use its scanline counter.
 

Mugi

Member
well, speaking from experience, i can say that implementing a makeshitft scanline counter out of DMC irq is indeed doable (im using it) but it comes with a lot of work that has to be put
into the engine, as in it's vanilla state you have zero chance to use it for anything, the engine is just too slow, and having it intentionally slow down to synchronize the CPU in order to actually do accurate splits
requires a relatively ridiculous amount of rewriting of things.

that said, if you peek in to the Dimension Shift WIP thread, you can see it can actually be done.

on the matter of other things, if you do make a writeup about setting up MMC3 for nesmaker, that'd be an interesting read.
 

Kasumi

New member
I know you have done it, I had you in mind as I wrote the post. ("for anyone not using mapper 30 features it doesn't support") You are, the CHR RAM bank switching.

I wrote out how to do it in the discord, the only real "issues" are
1. That a mapper 30 bank switch is 16KB, and an MMC3 bank switch is 8KB. So the NMI gets slightly more complicated to "interrupt" in the middle of the two writes safely.
2. Only $E000-$FFFF are guaranteed to consistent on startup (again, because the banks are 8KB), and NES Maker's engine has reset at $C000. This is only lightly annoying, the first issue is larger.

Beyond that, it's just change the header/bank switch routine and you're done. I'll add quotes from discord here:
Itemized changes (plain English)
1. Change mapper number to 4 from 30.
2. Change the name of the RESET label
3. Add a snippet before the vector includes called RESET that jmps to the new name of the old RESET
4. Add three bytes somewhere in RAM (nmisafeprg0, nmisafeprg1, and nmibanktoset)
5. Replace BankSwitch.asm with the 616 byte one below.
6. After the php left for context in the NMI, add the snippet from below.
7. Before the plp left for conext in the NMI, add the snippet from below.
8. Maybe initialize CHR banks too, I don't know if it's required. I only have an MMC3 CHR ROM cart.
Header.asm change (accomplishes 1):
Code:
	.db "NES",$1a       ;iNES identifier

	.db $20         ;number of PRG-ROM blocks
	;;32 prg rom blocks
	.db $00         ;number of CHR-ROM blocks
	;;0 chr rom blocks, using chr ram
	.db %01000011 
	;;   ||||
	    ;MAP 3 2 1 0
	
	.db %00000000    ;ROM control bytes: Horizontal mirroring, no SRAM or trainer, Mapper #0
	;;   ||||
	   ; MAP - 7 6 5 4
	
	.db $00,$00,$00,$00,$00,$00,$00,$00   ;filler

Reset Changes: (Accomplishes 2 and 3)
you could change RESET: in reset.ASM to RESETPOSTMAPPER: then add this directly below the include for the vectors in MainASM.asm:
Code:
;The below routine is 21 bytes.
;Vectors.ASM .orgs to $FFF0
.org $FFF0-21;So put this code 21 bytes before it
RESET:
    sei;Disable IRQs
    ldx #$00
    stx $E000;Disable mapper IRQs
    stx $A000;Set up mirroring
    
    stx $2000; Disable NMI
    stx $2001; Disable Rendering    
    stx $8000;Ensures $8000-$BFFF is swappable

    JMP RESETPOSTMAPPER
Accomplishes 5:
View attachment BankSwitch.asm.zip

Accomplishes 6:
Code:
NMI:
    ;first push whatever is in the accumulator to the stack
    
    PHA
    LDA doNMI
    BEQ dontSkipNMI
    JMP skipWholeNMI
dontSkipNMI:

    LDA #$01
    STA doNMI
    TXA
    PHA
    TYA
    PHA
    PHP;This line and above are unchanged, here for context

    lda nmibanktoset;Pushing nmibanktoset to the stack just to preserve it since we're still using
    pha;the generic bank switch routines which change it
    lda nmisafeprg0
    pha
    lda nmisafeprg1
    pha
    
    LDA temp;this line and below are unchanged, here for context
    PHA ;STA NMItemp
    LDA temp1

Accomplishes 7:
Code:
LDA #$00
    STA doNMI;This line and above are unchanged, here for context

    lda #7
    sta $8000

    pla
    sta nmisafeprg1
    sta $8001

    lda #6
    sta $8000
   
    pla
    sta nmisafeprg0
    sta $8001

    pla
    sta nmibanktoset
    sta $8000
    
    PLP;This line and below are unchanged, here for context
    PLA
    TAY
    PLA
    TAX
skipWholeNMI:    
    PLA

    
    RTI
There's a better way to do most of that, because NES Maker doesn't ever swap 8KB banks (it can't, and Indivisible does), but I leave it as an exercise to the reader if they really want the byte(s) of RAM

It's definitely possible to do 4, 5, 6, and 7 with fewer changes (because NES Maker will always swap two 8KB banks), but paradoxically I'd have to think harder about them. I know what Indivisible does works. 2 and 3 (and 8?) are lightly annoying, if only mappers initialized to a more known state they wouldn't be needed at all.

I'll add the caveat from discord as well:
Not super tested, but I'd anticipate problems would not be hard to fix
Edit: Hang on, reset is slightly wrong, I'll fix.
Edit4: Okay. Should be good. The previous version "worked" by accident. (Was using sta, but ldx which set not-the-mirroring-Indivisible-uses (0), assuming an emulator started A at 0.) I had wondered about why mirroring worked whenever I first did this, since NES Maker doesn't want its mirroring.
Edit2: The bank switch isn't the NMI safe one too >_> I guess I grabbed the wrong version from discord.
Edit3: Bank Switch fixed.

All issues should now be fixed, if they're not, yell at me.
 

Mugi

Member
that's pretty throughout for a "quick writeup", but then again, i suppose you do know what you're doing afterall, unlike i do :p
i already decided that dimension shift will be version locked to nesmaker 4.1.4 and mapper 30 as so much work has gone into the engine that simply flipping all that over at this point would
just be complete nonsense, but this is extremely interesting regarding any future endeavors i might do after DS.
scanline counter was one of my biggest butthurts with nesmaker's decision of going for mapper 30 (i love parallax THAT much!) but since that has now been rectified too in a relatively functional manner (work load aside lol)
i'll just keep that for future adventures :p

no wonder i missed it i guess, since i got (unjustly) banned from the NM discord :p
 

Kasumi

New member
The thoroughness is to be as safe as possible, but by changing only the bank switch subroutine/header I had a NES Maker game working on MMC3 in two minutes. Edit: (That said, it wasn't safe yet!) The writeups take much longer to read/write than actually doing it.

It's very few actual changes, at the end of the day. But still, anyone feel free to yell at me if this doesn't work for some reason. Edit2: Just realize if you DO do it, your game will only work exactly the same as it did before. This gives you the option to use MMC3 features, it doesn't provide anything that actually uses them.
 

Mugi

Member
well yeah obviously, the bigger workload here is to actually code something to make use of the provided things.
i was originally interested of chr rom as opposed to chr ram simply because that's what shatterhand uses, and my initial desire was just to make a shatterhand 2.
i eventually ditched that idea mostly due to copyright reasons but bleh..
chr ram is fun and all though, especially now since the way DS allocates it's chr's has been completely overhauled and void of all the nesmaker restrictions (i load full 128x128 chr's, with 16 metatiles of animations, no more huds and paths and screentiles.)
 

Kasumi

New member
I think my issues with writing all manner of NES Maker specific guides are time spent to write accounting for prerequisite knowledge, breaking the UI, and unfamiliarity with the engine.

It (probably) takes less time to set up an MMC3 IRQ than it does to do the changes outlined to change to MMC3 in the first place. You cli someplace safe, you remove the old waits for sprite 0, you move the code that actually changes the scroll into an IRQ (pushing A to/pulling A from the stack), then write how many scanlines to wait to three registers in order at the start of the NMI (if I remember correctly) and retime the code that's now in the IRQ slightly (this particular part would be annoying, if not for Mesen's Event viewer). This is actually a change you could make without really breaking the UI (the sprite zero position is exported, which you can use for how many lines to wait). It wouldn't work exactly the same (since a hit could occur of any of the 64 pixels within the sprite rather than the one exported position, depending on how the user sets it up), but that's mostly fine.

Maybe I could do it in 5 minutes! Certainly faster than it took to even write the above short version >_>. But if the discord MMC3 guide is any indication, it's gonna take a couple of hours to specifically write about how to do it/test it. And after I have done all that, there's no (real) benefit to anyone. (Well, maybe to people who want their HUD on the bottom.) Now you're on MMC3, and you're using a scanline split, but your game plays exactly the same as before you started. :lol: And then writing about multiple splits from there stops being about NES Maker, doesn't allow the UI to help etc.

I dunno, most things people ask about to me make me just want teach fundamentals in the first place. It usually stops being NES Maker specific by step 2, and then continuing in a NES Maker context makes the guide take more time to write for the reasons outlined above. (This post isn't really directed at anyone, just kind of a thought dump. I realize no one asked about then using scanline splits with MMC3, but like... it does end up being the next step. And the above explains why I end up not just covering that "obvious" next step.)

Edit: I guess one example people might understand is GGSound vs NES Maker's use of GGSound. (Order 09 limitations from earlier versions, things like that.) If I could teach how to use GGSound/IRQ Scanlines/Saving/whatever, and fundamentals could be used to apply them to NES Maker, I feel like more people could be helped. Otherwise, I'm bandaiding things, I feel. One could follow those MMC3 steps, and get a result, but with fundamentals you could switch to FME-7 or whatever. Someday, I'll write fundamental guides, but at this point I'm just ranting. <crawls back into hole>
 

Mugi

Member
i certainly feel you with that.
there's a reason why i didnt dump the code for CHR RAM to the forum or why i dont have a tutorial up for setting up a 4-directional scroll into nesmaker,
simply because the way those are done in my game are so out of nesmaker now that even if i did manage to fight my way through 10 hours of writing instructions and copypaste-ready code,
its still not manageable in any shape or form from nesmaker itself, it's just assembly code that nesmaker doesnt really know exists outside of the fact that it compiles it because it's included in a bank**.asm file.

someone even flipped a table at me that i lied when i said that my game doesnt really work with nesmaker anymore because i had it open on a video where i demonstrated something, and i just think people really misunderstand
the whole function of the tool. it doesnt have to be "if or else" scenario really.
for instance, the only things i actually use nesmaker for anymore are 1) drawing screens (i do assets and chr's outside of the tool, i literally just paint screens) 2) i manage objects with it 3) i use it to organize my assembly files.
i dont have hud things (clicking that on my project crashes nesmaker) i dont have UI options for managing variables, etc etc... there's so many things that are just stripped off or modified to the point that nothing really works the way it should.
 
Top Bottom