Learn how to make an endless runner for the Sega Mega Drive using SGDK! I highly recommend working through the Megapong Tutorial first, as that explains a lot of the basics of MD coding.

Megarunner 3 - Scrolling

Posted November 18, 2019

Welcome back! Last time we drew a background with a floor and even some street lights. This time we’ll get things moving by scrolling the background to create the illusion of endless running!


As usual, we have to do a bit of preparation work. First off we’ll define a new constant at the top of the file that will store the speed at which our background will scroll.

const int scrollspeed = 2;

Next up we’ll set our desired scrolling mode. In main(), add this line after VDP_setPlanSize(32,32);:


What does this actually do? First of all, note that we’re giving the function two values: The first one sets the desired mode for horizontal scrolling (HSCROLL) the second one for vertical scrolling (VSCROLL). But what is a mode? SGDK (and the Mega Drive hardware) supports multiple ways of scrolling. The values that we have chosen (with _PLANE) tell SGDK that we want to scroll the entire plane at once. You could also scroll tiles or lines, which are slightly more advanced modes that allow you to create parallax effects and such. But we simply want to scroll the entire plane for now.

One more thing before we get to the actual scrolling. We will need a variable that stores the scrolling offset, which I’ll explain in a second. Define this variable right before your main game loop starts:

int offset = 0;
    //game loop stuff

Keep Scrollin' Scrollin' Scrollin' Scrollin'

Alright, let’s get scrolling! As you might expect, SGDK offers us a function that will handle the scrolling. Put this one right at the beginning of your game loop:

VDP_setHorizontalScroll(BG_B, offset -= scrollspeed);

The first argument tells the function which plane we want to scroll. Since we put our tiles on background plane BG_B, that’s of course the one we want. The second argument passes in the offset that we want. This basically tells SGDK how far we want our plane to be moved: In our case, we want to move it by scrollspeed. But why are we keeping track of offset?

It might seem obvious to some and confusing to others, depending on what game engines you’ve worked with in the past. In some cases you tell the engine “I want the background to constantly scroll at speed X” and then it just happens. In SGDK this works differently! We are not passing in the desired speed at which we want the plane to scroll, but the actual distance we want it to move each frame. This means that we have to keep track of how far the plane has already scrolled, so that we can adapt that value for the next frame.

In the first frame, we have offset == 0. Our call to VDP_setHorizontalScroll subtracts scrollspeed from that, which results in offset == -2. This offset is then applied to the plane, meaning that it will be moved two pixels to the left. In the next frame, offset is reduced again to offset == -4, meaning the plane is now rendered another 2 pixels to the left (and 4 pixels left from where it originally was). So basically we have to tell SGDK every frame where our plane is currently located.

And one last thing: On the Mega Drive, planes loop automatically. Simply put, this means that any pixels that leave the left edge of the plane are automatically rendered on the right edge. This means that our background will never really move outside of the screen.

I hope that wasn’t too confusing.


“But wait!” I hear you cry. “Wouldn’t that mean that offset will keep decreasing forever”? Yes it does! And this is bad, because that would eventually cause an underflow and mess everything up. True, the game would have to go on for quite a while before we’ve reached the limit of what an int can take, but it’s better to be safe than sorry. So right after VDP_setHorizontalScroll(BG_B, offset -= scrollspeed); add the following line:

if(offset <= -256) offset = 0;

This resets offset so that it doesn’t become too small. Wonder why I picked 256? Well…try to figure it out yourself for a minute! Remember that plane size…?

Okay, I’ll tell you the answer. We set our plane size to be 32 tiles wide. One tile is 8px x 8px, meaning that our plane is 32 * 8 = 256 pixels wide. So if the plane has moved 256 pixels to the left (that is, when offset == -256), it will have moved one entire plane-length – and since it loops, it will basically be right back to where it started. So by resetting offset when it hits 256 we get smooth scrolling without any skips! (And just for fun, feel free to try out other values and see what happens).

That was a lot of theory, but if you compile the game now you should see that we have a scrolling background! Just look at it go. Feel free to make racing car noises! Neeeeoooown…


And here’s a question!

As you might remember we’ve only placed one streetlight in our scene…but now, sometimes there are two on screen at the same time! Do you know why? Think about it and post your answer in the comments below!

If you've got problems or questions, join the official SGDK Discord! It's full of people a lot smarter and skilled than me. Of course you're also welcome to just hang out and have fun!

Download the Project Files!

All patrons on Patreon get the complete source code for this tutorial, as well as other perks such as early access! And Patreon support also ensures that I can keep working on tutorials like this one. Become a Patron!
Just Want to Buy Me a Coffee?

Check out the rest of this tutorial series!

  • Megarunner 1 - The Framework
  • Megarunner 2 - Tiles
  • Megarunner 3 - Scrolling
  • Megarunner 4 - Player and Obstacles
  • Megarunner 5 - Jumping Math
  • Megarunner 6 - Collision and Score
  • Megarunner BONUS - Tile Scrolling
  • Get Words in Your Inbox!

    Be oldschool and sign up for my newsletter to get updates! Just enter your email address, prove you're not part of Skynet and you're good to go!

    Powered by CleverReach. I will not send you spam or sell/give your email address to someone else.  You can of course unsubscribe at any time. By clicking the subscribe button above, you confirm that you have read and agreed to our privacy policy.

    By using the Disqus service you confirm that you have read and agreed to the privacy policy.

    comments powered by Disqus

    Related Posts

    HaxeFlixel Tutorials!

    If you’ve popped over to the tutorial section recently you might have noticed that I’ve added my very first HaxeFlixel tutorial! It shows how to implement a simple, pixel-perfect 2D water shader which I used for Go! Go! PogoGirl. But a few of you might be wondering what a HaxeFlixel is. Well, it’s a 2D game framework that is as powerful as it is underrated! It runs on the (also underrated) Haxe language, is extremely well documented, open source, and has built-in functions for almost anything you’d need.
    Read More

    Streets of Was

    As I’m sure many of you will remember, the original Streets of Rage for the Mega Drive had multiple endings. The real canonical ending has you beat the crap out of Mr. X, thereby ending his reign of terror forever (yeah, right). However, if you confronted Mr. X with a buddy in tow, a new possible path unlocked. A quick refresher is in order. When you confront Mr. X he will ask you to join his organization.
    Read More

    Streets of Rage 2 Design Docs

    A few years ago, Yuzo Koshiro posted a pile of old game design documents for Bare Knuckle 2 aka Streets of Rage 2 on the Ancient blog to commemorate the release of Streets of Rage 2 3D on the Nintendo 3DS. These documents gave a deep insight into the game’s inner workings, technical aspects, designs and even some cut content. They were an awesome resource for one of the most awesome games ever created.

    Read More