Horizontal Scrolling

Thought I would throw this little routine up for anyone building a sideways scrolling game (as I am!).

It scrolls the screen scrollIncrement pixels to the left. Perform the scroll before rendering anytrhing else then do the usual arduboy.display().

It is very fast though I have not done any benchmarking.

Hope it helps.

#define BUFFER_ROW_0_START    0

void scrollBuffer(byte scrollIncrement) { 

  memmove (arduboy.sBuffer + BUFFER_ROW_0_START, arduboy.sBuffer + BUFFER_ROW_0_START + scrollIncrement, WIDTH - scrollIncrement);
  memmove (arduboy.sBuffer + BUFFER_ROW_1_START, arduboy.sBuffer + BUFFER_ROW_1_START + scrollIncrement, WIDTH - scrollIncrement);
  memmove (arduboy.sBuffer + BUFFER_ROW_2_START, arduboy.sBuffer + BUFFER_ROW_2_START + scrollIncrement, WIDTH - scrollIncrement);
  memmove (arduboy.sBuffer + BUFFER_ROW_3_START, arduboy.sBuffer + BUFFER_ROW_3_START + scrollIncrement, WIDTH - scrollIncrement);
  memmove (arduboy.sBuffer + BUFFER_ROW_4_START, arduboy.sBuffer + BUFFER_ROW_4_START + scrollIncrement, WIDTH - scrollIncrement);
  memmove (arduboy.sBuffer + BUFFER_ROW_5_START, arduboy.sBuffer + BUFFER_ROW_5_START + scrollIncrement, WIDTH - scrollIncrement);
  memmove (arduboy.sBuffer + BUFFER_ROW_6_START, arduboy.sBuffer + BUFFER_ROW_6_START + scrollIncrement, WIDTH - scrollIncrement);
  memmove (arduboy.sBuffer + BUFFER_ROW_7_START, arduboy.sBuffer + BUFFER_ROW_7_START + scrollIncrement, WIDTH - scrollIncrement);

  memset (arduboy.sBuffer + BUFFER_ROW_0_START + WIDTH - scrollIncrement, 0, scrollIncrement);
  memset (arduboy.sBuffer + BUFFER_ROW_1_START + WIDTH - scrollIncrement, 0, scrollIncrement);
  memset (arduboy.sBuffer + BUFFER_ROW_2_START + WIDTH - scrollIncrement, 0, scrollIncrement);
  memset (arduboy.sBuffer + BUFFER_ROW_3_START + WIDTH - scrollIncrement, 0, scrollIncrement);
  memset (arduboy.sBuffer + BUFFER_ROW_4_START + WIDTH - scrollIncrement, 0, scrollIncrement);
  memset (arduboy.sBuffer + BUFFER_ROW_5_START + WIDTH - scrollIncrement, 0, scrollIncrement);
  memset (arduboy.sBuffer + BUFFER_ROW_6_START + WIDTH - scrollIncrement, 0, scrollIncrement);
  memset (arduboy.sBuffer + BUFFER_ROW_7_START + WIDTH - scrollIncrement, 0, scrollIncrement);


A handy function, thanks for sharing it.
(No doubt there’s now going to be an upsurge of sidescrollers.)

Out of interest Arduboy’s screen actually has a horizontal scroll feature but I’m not sure how it works (so it might not be as suitable), but it’s worth mentioning because it’s probably faster.

It’s mentioned on page 28 of the screen’s datasheet. I always find hardware documents awkard to read though.

From what I can tell the command sequence (to be sent over SPI) would be:
0x27 - Scroll Left
0x00 - Padding
0x00-0x07 - Page Address
0x00-0x07 - Scroll Delay (selected from a table)
0x00-0x07 - End Page Address
0x00 - Padding
0xFF - Padding

Like I say though, might not be as suitable because it’s affecting the screen rather than the buffer.

Perhaps you should fork Arduboy2, add it in and then see if you can get it accepted via pull request. I for one think it would be a good addition to the library.

It’s not suitable. Hardware scroll just starts scrolling the display at a constant rate dependent on the display’s internal clock (which can vary slightly from unit to unit). Once started you get no feedback on how far it has scrolled over a given time period. You can’t tell it to scroll by a fixed number of pixels and then stop.

It was actually @MLXXXp’s code for the scrolling logo that got me looking at this. I quickly discovered the issues Scott mentions with the biggest one being that you could not control the number of pixels it moved. If you could, you could scroll the screen then redraw whichever sprites needed updating. As it it, you can only guess.

Let’s hope so!

1 Like

Couldn’t this be a suitable case for parallax(or non-parallax) scrolling backgrounds that have no collision associated with them? Free up some buffer and have nice scrolling backgrounds :slight_smile: At the very least I could see a use for it in infinite runners.

Hrm, I don’t know.

I’m assuming probably not because it’s moving all the screen contents (characters as well as the background). Also the document later says that reading from and writing to (the area of RAM where the display buffer is located) is prohibited during scrolling.

Generally though the datasheet tells you loads of numbers and spec details but doesn’t actually fully explain how scrolling operates.
Without actually testing it to see how it behaves I wouldn’t know.

It was a nice idea at the time, but it seems the scrolling features are intended only for screensavers or simple scrolling graphics, nothing interactive.

Hardware horizontal scroll just starts horizontally scrolling the contents of the display’s local RAM buffer, bounded by the area you specify. You can also specify the scrolling speed, but this speed is related to the display’s internal clock, which is not in sync with the processor clock, and varies somewhat from unit to unit and over battery voltage and temperature.

Over any given time period, it’s impossible for the processor to know precisely how far the display has scrolled.

And, you’ve already stated the limitation of not being able to change the display contents while scrolling is in operation.

It might be good for a scrolling prompt banner (like Game Over) while waiting for a button press, but that’s about it.

1 Like

Don’t tell me, tell @silatjedi.

Hardware scroll isn’t good for much of anything except for some very specific cases.

1 Like

I wrote this for a sideways scrolling game I was planning. As it turns out, it did not work for me as the effort required to erase the previous player and enemy images, scroll the background and repaint the same images was similar to clearing the screen and repainting everything.

As discussed, this is probably a better solution than the hardware scrolling if you need to know exactly how many pixels it has scrolled or want to add details (maybe some new stars in a sky-scape) as the screen scrolls. Of course, if your game does not involve the enemies moving up or down and the re-rendering of the player overwrites the original image as they move then this will work a treat.

1 Like