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 BONUS - Tile Scrolling

Posted December 16, 2019

Know what makes any game better? Parallax scrolling! I know that might sound like an overstatement, because it is. But parallax scrolling is really cool, so let’s add a small parallax effect to our game!


First, let me explain what we’re gonna do here. Remember when I talked about the different scrolling modes? So far we’ve used plane scrolling, but for a parallax effect we’ll have to go one step further and use tile scrolling. As the name implies, this lets us scroll individual rows of tiles, rather than the whole plane at once. By letting rows scroll at different speeds, we create the parallax effect. This will also require some changes in how we deal with the offset. Oh and of course we’ll actually have to add some more tiles that we can scroll. All in all it’s not really difficult. Let’s get going!

Adding Tiles

First let’s add some new tiles that we’ll scroll at a different speed. These will appear to be in front of our old tiles, thus they will scroll faster. Go to the place in main() where we set all our tiles using VDP_fillTileMapRect and VDP_fillTileMapRectInc. Replace the three lines we had with these ones:

VDP_fillTileMapRect(BG_B, TILE_ATTR_FULL(PAL1,0,FALSE,FALSE,1),0,16,32,1); //Floor 
VDP_fillTileMapRect(BG_B, TILE_ATTR_FULL(PAL1,0,FALSE,TRUE,2),0,17,32,2); //Wall

VDP_fillTileMapRect(BG_B, TILE_ATTR_FULL(PAL1,0,FALSE,FALSE,1),0,19,32,1); //Floor in the front
VDP_fillTileMapRect(BG_B, TILE_ATTR_FULL(PAL1,0,FALSE,TRUE,2),0,20,32,8); //Wall in the front

VDP_fillTileMapRectInc(BG_B,TILE_ATTR_FULL(PAL1,0,FALSE,FALSE,3),15,13,2,3); //Lights

This will add another “layer” in front of our current one. It might be a bit hard to visualize, so if you’re having trouble, compile and check the game after adding each line. You’ll see how the whole thing is structured!



If you try the game out now, you’ll see that everything still scrolls uniformly as it did before. Let’s change that!

First, the most obvious thing: Change the scrolling mode from PLANE to TILE:


Next we will need a new variable to handle our new scrolling mode. Add this line before the while(1) loop:

s16 scrollValues[15] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };

This line creates an array of s16 values (signed shorts, so integer values that range from -32,768 to 32,767) with a length of 15 members. Why 15? Each value in the array determines the scrolling offset for an individual row of tiles. The upper half of our game screen consists of single-color sky that we won’t have to scroll, so we can ignore those rows. And our actual floor tiles (including the lights) make up a total of …15 rows! That’s 3 rows for the lights, 2 rows for our two floors and 10 rows for the wall tiles.

Alright, before we get to the actual scrolling, define one last variable right after the previous one:

int i = 0;

And also delete the line int offset = 0; as we won’t be needing that variable anymore.

Okay, now let’s dive into the game loop and get those tiles moving! At the beginning of while(1), delete the following two lines:

VDP_setHorizontalScroll(BG_B, offset -= scrollspeed);
if(offset <= -256) offset = 0;

Instead, replace them with this one:


This function takes care of the tile scrolling and is a bit more complex than its plane counterpart. It takes the following arguments:

  1. The plane we want to do our scrolling on
  2. The first tile row we want to scroll; in our case the 13th row from the top, which is where our street lights start
  3. A pointer to the first element of an array with scroll values. In practice you can just put the array variable itself in here
  4. The total number of rows we want to scroll; we have 15 values so we want to scroll 15 rows
  5. The transfer method, which can be either CPU or DMA. CPU is fine for our purposes.

So far so good. But this will actually only scroll our tile rows once, because we have to tell the game to scroll the tiles every frame. So after the previous line, add this for loop:

for(i=0; i<15; i++){
    if(i <= 5){
        scrollValues[i] -= scrollspeed;
    } else{
        scrollValues[i] -= (scrollspeed+1);
    if(scrollValues[i] <= -256) scrollValues[i] = 0;

Let’s break it down. First off, you’ll see that we’re using that variable i that we’ve defined. We’re using it to iterate through all 15 elements of our array.

We want to shift the first 6 rows (remember, arrays in C start at index 0) left by scrollspeed every frame. These rows make up our street lights and the main floor and wall tiles.

The remaining tiles make up the new front layer, which we want to scroll faster; one pixel faster than the regular scroll speed, to be precise.

Finally, just like we did with offset, we check whether a scroll value has gone below -256 and reset it to 0 if it has. This prevents underflows. Check step 3 of this tutorial for a more detailed explanation.

And yeah…that’s pretty much it! Instead of scrolling the entire plane at once, we’re now scrolling each tile row individually. And while this sounds like a lot of extra work, SGDK makes it rather easy for us: Basically we just need to work with an array of values instead of a single value.


Thank you for reading and thank you even more for supporting me on Patreon! It means a lot to me and I hope I can keep doing tutorials like this for a long time to come. Until next time and be excellent to each other!

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!

Take It to the Next Level!

Want more tutorials like this one? Want sneak peeks, early access and more? Then consider supporting me on Patreon!

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