Parallax


Something I'd been thinking about trying for a long time in Doc 2 was adding some parallax to the background. This would serve two purposes, firstly it would give a greater sense of depth and really enhance the visuals, and secondly it would reduce the tile count by removing the need for background tiles with shadows of the foreground as the only reason for those was to create a simple illusion of depth. After a bit of experimentation I've finally reached a version I'm happy with. So let's talk a little bit about what decisions were made and how it was implemented:


Methods

Landscape Parallax

For games that scroll left and right with repetitive landscape backgrounds that have no depth overlap (each horizontal section contains only one depth which is continuous across the whole screen) there is an easy trick which is probably one of the first methods used on the C64 to create a parallax effect. The method involves splitting the screen into several bands using a raster interrupt and scrolling them horizontally at different speeds. By scrolling from slow to fast from top to bottom you can create the illusion of a deep landscape. Additional effects to enhance the result include scrolling the "bands" vertically a little during Y movements to create a more 3D effect, or using sprite mutiplexing to create a foreground layer that scrolls over the whole scene (such as passing trees, lamp posts etc).

   

Wrath of the Demon and Silkworm

Multiple Charsets

A very effective way of creating a highly detailed parallax background is to use multiple character sets. The way this works is a repeating background pattern spanning multiple characters is created, this can comprise of a large number of characters and the performance remains the same. Then for each scrolled position a shifted version of this charset is created (this is 4 positions for horizontal scrolling in MC mode) during the screen scroll the charset is switched in order to create the illusion of the background pattern scrolling independently of the foreground. When the characters have fully scrolled 8 pixels then the background sections on screen are shifted by 1 char in the desired direction and the process repeats. This method is used in games such as Flimbo's Quest, Hawkeye and the spaceship levels in Turrican 2, and it produces some of the most detailed parallax effects you will see in C64 games. Unfortunately its not something I can use as I want to scroll in 8 directions which would require more character sets than I have space for, Doc already uses 2 for the time travel effect and needs plenty of room for the double buffered screen and dual sprite sets.

      

Flimbo's Quest and Hawkeye

Animating Chars

It's my opinion that by far the most common way of creating a parallax in C64 games is by using character animations. The trick is simple, you create an animation at run time by shifting the 8 bytes of data around that make up a char. This works well for horizontally scrolling because the 6510 cpu has the ROL and ROR instructions which will do most of this work very quickly indeed (ROL and ROR are 9 bit rolls so you need a little extra to make it 8bit, but I'll demonstrate that below). For vertical scrolling it's a tiny bit more complex but not much, in this case we shift the 8 bytes rather than the bits in each byte. Vertical scrolling takes a few more cycles than horizontal but not by much. You can combine the shifts of multiple chars as cycle time permits to create "tiles" that can be scrolled independently of the map. The great thing with this is you perform the scroll on the characters and it affects every instance of that on screen. This effect is used to great effect in many games such as Creatures, Mayhem in Monsterland, Parallax, Turrican and a lot more. I decided this is the only method suitable for Docs 8 way scrolling and so after a little experimentation have implemented this into the game.

   

Mayhem in Monsterland and Turrican II

The Code

The following code shows an example of scrolling two characters laid out in a brickwork pattern, using the method I settled on. The trick to making the brick pattern work is to scroll horizontally across the 16 bits covering the two chars, and when shifting bytes vertically to roll the bytes that should wrap around onto the opposite char to maintain the alternate brickwork rows. Code is done using KickAssemblers for loops to unroll the code for speed (apologies in advance for the awful code formatting on here!)

/*
    Scrolls the two chars at $f138 and $f140 horizontally using 16 bit rolls  
*/
!ScrollLeft:
            .for(var i=0; i< 8; i++) {
                lda $f138 + i
                rol
                rol $f140 + i //Rolls the carry from the first ROL into bit 0
                adc #$0 //puts the carry flag into bit 0 of the first char
                sta $f138 + i
            }
            rts


!ScrollRight:
            .for(var i=0; i< 8; i++) {
                clc
                lda $f138 + i
                ror    
                ror $f140 + i //Rolls the carry from the first ROR into bit 7
                bcc !+
                ora #$80 //puts the carry flag (if set) into bit 7 of the first char
                !:
                sta $f138 + i
            }       
            rts


/*
Scrolls the two chars at $f138 and $f140 vertically with wrapping across opposite chars to 
create the brickwork pattern
*/
!ScrollUp:
            ldx $f138 //Store the top byte in the chars
            ldy $f140
            .for(var i=1; i< 8; i++) {
                lda $f138 + i    //Shift the char bytes up
                sta $f137 + i
                lda $f140 + i
                sta $f13f + i
            }
            stx $f147 //Put the stored chars into the bottom of the opposite chars
            sty $f13f
            rts
!ScrollDown:
            ldx $f13f //Store the bottom byte in the chars
            ldy $f147
            .for(var i=6; i>=0; i--) {
                lda $f138 + i     //Shift the char bytes down
                sta $f139 + i
                lda $f140 + i
                sta $f141 + i
            }
            stx $f140 //Put the stored chars into the top of the opposite chars
            sty $f138 
            rts


Depending on your particular use case this code can be adjusted to use more or less chars, and alter the vertical pattern. You may also want to sacrifice a little speed for flexibility and use indirect zero page instructions instead of hardcoded values and unrolled loops.


Considerations

As well as the performance considerations there are a couple of important things to consider. Firstly while Doc himself moves with no inertia, the map scroller I am using does have smoothing. This means it can potentially go from 4 to 1 pixel per frame scrolling over time and so the parallax effect needs to match this. The method I settled on was to have the parallax move at most 1 pixel per frame when the map is moving at full speed and to skip frames at slower speeds. This way I can avoid having to shift too much per frame and the effect is slow enough to give the nice illusion of depth.

Secondly the colour of the chars used in the effect is important. I've gone for colours that are both muted but contrast well with the foreground so that when static they do not clash and cause confusion. For this same reason I've chosen to go with high resolution single colour chars rather than multi colour as this also assists with creating the feeling of a separate distant layer.

Finally the shapes used need to work with the tiles in the foreground. If the foreground tiles have a lot of dead space around them such as the circular pipe outlets on my industrial tile set then the pattern used for the parallax chars should also have a lot of dead space as this helps to hide the fact that the parallax doesn't actually go behind any tiles and limit the visible "black box" artifacts around said tiles. On my Aztec tile set the tiles are much more square in their design and so the parallax character can be much more intricate and with less black space.


Note how the black space around the pipes and outlets is masked by the more sparse parallax "blue brick" char pattern 

Whereas the Aztec tileset can use a more intricate pattern due to the relatively square edges of the tiles.


Next Time

That's all for this time. I've posted this a little earlier than planned as I'm expecting there to be a while before I can post another good update. Next time I'll be going over the gameplay in Doc , what you can expect from the game and how some of the gameplay design decisions have been made. So until then, cheerio!

Comments

Log in with itch.io to leave a comment.

Cool bananas mate! Thanks for sharing more of the behind the scenes for us :-)

Buena pinta tiene esto!