Add Depth to Your Game with Parallax Scrolling

Oct 21st in Games by Michael Williams

This is a really simple technique which you can use to add the illusion of depth to any side-scroller game. Whilst I'm explaining how it's achieved, we'll also take a look at making infinitely repeating backgrounds.

PG

Author: Michael Williams

Michael is a freelance Flash developer and technical writer. He writes AS3 game development tutorials on his blog, and is also on Twitter: @MichaelJW.

Introduction

You can see the basic parallax scrolling effect at work in the demo. By adjusting the speeds at which certain objects scroll, we can change how near or far away they appear to be.

This tutorial will explain how to code the effect, as well as how to make the game's camera appear to follow the car. Lastly, it'll explain how to create infinitely repeating backgrounds, just like in Scooby Doo.

Step 1: The Setup

If you're using Flash, create a new ActionScript 3.0 Flash file. Set the size of the stage to be whatever you like; I've chosen the default 550 by 400 pixels.

If you're not using the Flash IDE, don't worry; just create a new AS3 project. The zip file contains a SWC with all my graphics, so you can use those just by adding it to your library. Skip all the drawing steps if you do so.

If you are using the Flash IDE but you don't want to draw anything, the zip also contains a FLA file containing all my MovieClips :)

Step 2: Draw a Car

Create a new MovieClip symbol and draw a car. You can animate it if you like. Here's mine:

Car symbol

Try to centre the car so that its registration point (the little cross) is about half-way along. This will make it easier for the camera to follow it later.

Step 3: Export Your Car for ActionScript

Right-click your car symbol in the Library and select Properties:

Car properties

Give your car a Class (Car will do) and check the Export for ActionScript box (this lets us access the car using code). Also, check the Export in first frame box (otherwise, we'll have to make a preloader).

Step 4: Draw a Road

Create another new symbol, but this time draw a road:

Road symbol

Make it wider than the stage, but unlike the car, align the registration point with the left edge of the road. This will help later on, when we need to turn it into a repeating pattern.

Just like you did for the car, give the road a Class, export it for ActionScript and export it in the first frame.

Step 5: Create the Document Class

Create a new AS file and paste the following code into it:

package 
{
	import flash.display.MovieClip;
	public class ParallaxDemo extends MovieClip 
	{
		public function ParallaxDemo()
		{
			
		}
	}
}

Save this as ParallaxDemo.as, in the same folder as your FLA (or as your project, if you're not using the IDE).

If you're using the IDE, be sure to set this as your document class in the Properties Panel of your document:

Setting the Document Class

Step 6: Set Up the Car and the Road

Create new instances of your car and road in the AS file: (lines 6, 7, 11, 12)

package 
{
	import flash.display.MovieClip;
	public class ParallaxDemo extends MovieClip 
	{
		public var car:Car;
		public var road:Road;
		
		public function ParallaxDemo()
		{
			car = new Car();
			road = new Road();
		}
	}
}

Step 7: Position the Car and the Road

If you're using my graphics, you can just copy the following code (lines 14-17:

package 
{
	import flash.display.MovieClip;
	public class ParallaxDemo extends MovieClip 
	{
		public var car:Car;
		public var road:Road;
		
		public function ParallaxDemo()
		{
			car = new Car();
			road = new Road();
			
			car.x = 275.0;
			car.y = 235.0;
			road.x = 0.0;
			road.y = 294.0;
		}
	}
}

Otherwise, you'll need to figure out where your car and road should be placed at the start. Create a new Layer in your FLA's timeline, then make it into a Guide Layer by right-clicking it and selecting Guide. Flash will ignore anything you do in this layer when it creates a SWF, so drag your car and road symbols here.

Make sure the left edge of your road is aligned with the left edge of the stage, and that the car is roughly in the centre (horizontally). Then adjust them so that they fit together:

Positioning the car and the road

Now take my code from above and adjust it to match the x- and y- coordinates of your car and road. By clicking on your car or road, you'll be able to see these values in the Properties panel.

Step 8: Add your Symbols to the Stage

If you test your movie now, you'll see nothing. We need to addChild() the car and road to the stage:

public function ParallaxDemo()
{
	car = new Car();
	road = new Road();
	
	car.x = 275.0;
	car.y = 235.0;
	road.x = 0.0;
	road.y = 294.0;
	
	stage.addChild( road );
	stage.addChild( car );
}

(Make sure you add the road first, or it'll cover the car!)

Test your movie now, and it should look like this:

Great! Well, OK, it's nothing spectacular yet. But now the setup's out of the way, we can make this more interesting. For starters, let's get this car moving...

Step 9: Add an ENTER_FRAME Event Listener

Alter your document class to add an event listener to be triggered on each frame: (lines 4, 23, 26-29)

package 
{
	import flash.display.MovieClip;
	import flash.events.Event;
	public class ParallaxDemo extends MovieClip 
	{
		public var car:Car;
		public var road:Road;
		
		public function ParallaxDemo()
		{
			car = new Car();
			road = new Road();
			
			car.x = 275.0;
			car.y = 235.0;
			road.x = 0.0;
			road.y = 294.0;
			
			stage.addChild( road );
			stage.addChild( car );
			
			addEventListener( Event.ENTER_FRAME, onEnterFrame );
		}
		
		public function onEnterFrame( evt:Event ):void
		{
			
		}
	}
}

If you kept the default frame rate of 12fps, the onEnterFrame() function will be called every 1/12th of a second.

Step 10: Move That Car!

If we keep increasing the car's x-position...

public function onEnterFrame( evt:Event ):void
{
	car.x = car.x + 10;
	//if you're up for a challenge, try adding basic
	//keyboard controls to let the player accelerate
	//and decelerate.
}

...we can make the car move forwards...

Exit, stage right

...right off the edge of the screen!

Step 11: Follow That Car!

This is hardly ideal; after a few seconds we can't even see the car any more. So let's make the "camera" appear to follow the car.

What exactly does this mean? Well, basically we need the car to stay in the same place, while the road appears to move backwards.

That means we could do something like this:

public function onEnterFrame( evt:Event ):void
{
	road.x = road.x - 10;
}

...but this would just complicate things later. For example, imagine if we wanted to add other cars to the road, or powerups, or oil slicks, or anything at all; we'd have to move every single one of them backwards in the onEnterFrame() function.

No, there is a much simpler technique we can use. Instead of addChild()-ing the car and road to the stage, we create a new object, addChild() them to that, and then move this object backwards in the onEnterFrame() function.

It sounds more complicated than it is. Let me show you the actual code:

package 
{
	import flash.display.MovieClip;
	import flash.events.Event;
	public class ParallaxDemo extends MovieClip 
	{
		public var car:Car;
		public var road:Road;
		public var roadContainer:MovieClip;
		
		public function ParallaxDemo()
		{
			car = new Car();
			road = new Road();
			
			car.x = 275.0;
			car.y = 235.0;
			road.x = 0.0;
			road.y = 294.0;
			
			roadContainer = new MovieClip();
			roadContainer.addChild( road );
			roadContainer.addChild( car );
			
			stage.addChild( roadContainer );
			addEventListener( Event.ENTER_FRAME, onEnterFrame );
		}
		
		public function onEnterFrame( evt:Event ):void
		{
			car.x = car.x + 10;
			roadContainer.x = roadContainer.x - 10;
		}
	}
}

In lines 9 and 21 we create a new, blank MovieClip called roadContainer. Flash automatically sets its x and y values to 0.

In lines 22 and 23 we add the road and the car to the roadContainer, instead of the stage. In line 25 we add the roadContainer itself to the stage - and since the car and road are now added to the roadContainer, this lets us see them on the stage.

Line 32 is the most important part. Here, we move the roadContainer backwards by the same amount that we just moved the car forwards. This means that everything inside the roadContainer gets moved left by 10 pixels, but since the car has just been moved 10 pixels to the right, it stays in the centre of the screen.

It's a bit like running up the down escalator. If you walk up at the same speed that it's moving down, then to a person standing on the stairs next to you, you don't seem to be moving.

The overall effect:

It's the end of the world as we know it, and I feel fine.

The car stays in the centre! Great. Well, great apart from the gaping white hole. But we'll get to that. Now if you want to add more cars to the road, all you have to do is addChild() them to the roadContainer.

Step 12: Improve the Following

The trouble with just moving the whole container backwards a little bit every frame is that it's not very flexible. What if the player uses a powerup to teleport the car 100 pixels forwards instead of 10? What if we want to make the camera centre on a different car?

When the SWF first loads, the car's x-position is 275, and the roadContainer's x-position is 0. How do they each change over time?

  • Start: car.x is 275, roadContainer.x is 0
  • Frame 1: car.x is 285, roadContainer.x is -10
  • Frame 2: car.x is 295, roadContainer.x is -20
  • Frame 3: car.x is 305, roadContainer.x is -30

Do you see a general rule connecting the two? If not, check this out:

  • Start: car.x is 275, roadContainer.x is 275 - 275
  • Frame 1: car.x is 285, roadContainer.x is 275 - 285
  • Frame 2: car.x is 295, roadContainer.x is 275 - 295
  • Frame 3: car.x is 305, roadContainer.x is 275 - 305

The connection is a little more obvious now! Let's put it into code:

public function onEnterFrame( evt:Event ):void
{
	car.x = car.x + 10;
	roadContainer.x = 275 - car.x;
}

You can do what you like with the car now. Speed it up, teleport it forwards a random number of pixels, stop it moving - whatever! The camera will still follow it.

And if you want to make the camera follow a different object, just replace car.x with otherObject.x in the line we just changed.

Step 13: Extend the Road

Time to fix the endless white void of nothingness at the end of the road.

The simplest way to make the road longer is just to add another instance of the road symbol to the right of our existing symbol, like so: (lines 9, 22, 23, 27)

package 
{
	import flash.display.MovieClip;
	import flash.events.Event;
	public class ParallaxDemo extends MovieClip 
	{
		public var car:Car;
		public var road:Road;
		public var road2:Road;
		public var roadContainer:MovieClip;
		
		public function ParallaxDemo()
		{
			car = new Car();
			road = new Road();
			road2 = new Road();
			
			car.x = 275.0;
			car.y = 235.0;
			road.x = 0.0;
			road.y = 294.0;
			road2.x = road.x + road.width;
			road2.y = road.y;
			
			roadContainer = new MovieClip();
			roadContainer.addChild( road );
			roadContainer.addChild( road2 );
			roadContainer.addChild( car );
			
			stage.addChild( roadContainer );
			addEventListener( Event.ENTER_FRAME, onEnterFrame );
		}
		
		public function onEnterFrame( evt:Event ):void
		{
			car.x = car.x + 10;
			roadContainer.x = 275 - car.x;
		}
	}
}

Here's how mine looks when it's run:

Road gap

Oh dear. Better fix that gap.

Step 14: Mind the Gap

(If you're not drawing your own graphics, skip to Step 17.)

The problem is in line 22 of the above code, road2.x = road.x + road.width. The road's width value must be slightly larger than my road actually appears to be.

Even if your road doesn't have the same problem, it still might not fit together perfectly. So head back to your FLA and drag another Road symbol from the library to your Guide layer.

Make sure it's got the same y-position as the first road segment, then move it along horizontally until there's no gap:

No gap

Step 15: Tweak the Join

If the edges of your two symbols don't quite join together neatly, double-click one of them. You'll be able to edit it and immediately see how the changes you make affect the other:

Use this trick to adjust the edges of the symbol so that the join is clean.

Step 16: Work Out the Breadth

Instead of using road.width to figure out where the second road segment should be placed, we'll use a number I call the breadth.

To find this number for your road, just take the x-position of your right-most road symbol (in your Guide layer), and subtract the x-position of your left-most road symbol.

All you're doing here is figuring out how many pixels apart your two road segments have to be in order to get the same perfect join you just created in Flash.

Step 17: Add a Breadth Variable

Create a new Number variable, roadBreadth, and set its value to the number you worked out in the previous step: (If you're using my graphics, that number is 653.7.)

public class ParallaxDemo extends MovieClip 
{
	public var car:Car;
	public var road:Road;
	public var road2:Road;
	public var roadContainer:MovieClip;
	public var roadBreadth:Number;
	
	public function ParallaxDemo()
	{
		car = new Car();
		road = new Road();
		road2 = new Road();
		roadBreadth = 653.7;

Step 18: Replace Width with Breadth

Replace the line:

road2.x = road.x + road.width;

with:

road2.x = road.x + roadBreadth;

Now test it out. There should be no gap:

Look Ma, no gap!

Great! We still run into the endless white void, though...

Step 19: Make the Background Repeat Infinitely

We could create a road3 and a road4 and a road5 and so on, positioning them each to the right of the one before them, but no matter how many segments we created, the car would reach the end of them eventually.

For a better solution, think back to when you were a child. Did you ever play that game where you pretend the floor is made of lava, but you have to get to the other end of the room somehow? (If not, heck, go play it now, it's great fun.)

I don't know about you, but in my house, sofa cushions were considered to be lava-resistant, able to be used as stepping-stones. We only had a couple, which wasn't enough to reach the end of the room - but eventually I figured out how to make them reach further.

I'd lay the two cushions down to make a short path, and walk over to the second one. Then, I'd pick up the one behind me, drop it in front of me, and step across to it. By repeatedly picking up the one behind me and moving it in front of me, I could get to anywhere I pleased.

We can use the same technique to make the road last forever, without having to use more than two segments. All we have to do is detect when a road segment is "behind" the camera, and move it in front of it.

What do I mean by "behind" the camera? I mean that the right-hand edge of the segment is off the left-hand edge of the stage. We can use this if-statement to check that:

if ( road.x + roadBreadth + roadContainer.x < 0 )
{
	//road is behind the camera
}

Curious as to why this works? If not, skip to the next step. Otherwise, let me explain:

  • road.x is how many pixels to the right the left-hand edge of road is from the left-hand edge of roadContainer
  • road.x + roadBreadth is how many pixels to the right the right-hand edge of road is from the left-hand edge of roadContainer
  • roadContainer.x is how many pixels to the right the left-hand edge of roadContainer is from the left-hand edge of the stage (since roadContainer is constantly moving to the left, this will usually be negative)
  • So, ( road.x + roadBreadth + roadContainer.x ) is how many pixels to the right the right-hand edge of road is from the left-hand edge of the stage.

Phew! OK, I'll admit, that's pretty confusing. If you'd like a deeper explanation, feel free to ask in the comments :)

Step 20: Move the Road in Front of the Camera

Now that we can tell when the road segment is behind the camera, we need to move it in front of the camera again.

If we moved the road to the right by roadBreadth number of pixels, it would be in the exact same place as the other road segment. So, we need to move it to the right by twice that amount:

if ( road.x + roadBreadth + roadContainer.x < 0 )
{
	road.x = road.x + (2 * roadBreadth);
}

Put that in your onEnterFrame() function, and test it out:

As you can see, one road segment is repeating, but the other isn't yet.

Step 21: Move the Other Road Segment

We can just copy the above code for our other road segment, road2:

public function onEnterFrame( evt:Event ):void
{
	car.x = car.x + 25;
	roadContainer.x = 275 - car.x;
	if ( road.x + roadBreadth + roadContainer.x < 0 )
	{
		road.x = road.x + (2 * roadBreadth);
	}
	if ( road2.x + roadBreadth + roadContainer.x < 0 )
	{
		road2.x = road2.x + (2 * roadBreadth);
	}
}

Test it out again:

Fantastic! An infinitely looping, side-scrolling background :) Now to create the actual parallax effect...

Step 22: Create Rolling Hills

(Skip this step if you're using my graphics.)

We're going to need a repeating background to show off the parallax scrolling. I've chosen hills, but you could make buildings, forests, alien sculptures - anything you like!

There are a few tricks you can use to make what you draw look like it's further away than the car:

  1. Use duller colours (for instance, a darker shade of green for your grass)
  2. Draw less detail (no individual tufts of grass)
  3. Add a "blurred" effect to the edges (because the camera is focused on the car)

Follow the same basic steps we used for drawing the road:

  • Make the symbol wider than the stage
  • Align the symbol so that the registration point is at the left-hand edge
  • Give it a class name, export it for ActionScript, and export it in the first frame
  • Tweak the join to make sure two symbols fit together neatly
  • Figure out the "breadth" of the symbol

Here's mine:

Green green hills

Step 23: Code the Hills

The code regarding the hills is almost exactly the same as the code we just wrote regarding the roads. Have a go at writing it yourself. I've pasted my AS file with all the new additions below, so you can refer to it if you like:

package 
{
	import flash.display.MovieClip;
	import flash.events.Event;
	public class ParallaxDemo extends MovieClip 
	{
		public var car:Car;
		public var road:Road;
		public var road2:Road;
		public var roadContainer:MovieClip;
		public var roadBreadth:Number;
		public var hills:Hills;
		public var hills2:Hills;
		public var hillsBreadth:Number;
		public var hillsContainer:MovieClip;
		
		public function ParallaxDemo()
		{
			car = new Car();
			road = new Road();
			road2 = new Road();
			roadBreadth = 653.7;
			
			hills = new Hills();
			hills2 = new Hills();
			hillsBreadth = 890.5;
			
			car.x = 275.0;
			car.y = 235.0;
			road.x = 0.0;
			road.y = 294.0;
			road2.x = road.x + roadBreadth;
			road2.y = road.y;
			
			hills.x = 0;
			hills.y = 14.5;
			hills2.x = hills.x + hillsBreadth;
			hills2.y = hills.y;
			
			roadContainer = new MovieClip();
			roadContainer.addChild( road );
			roadContainer.addChild( road2 );
			roadContainer.addChild( car );
			
			hillsContainer = new MovieClip();
			hillsContainer.addChild( hills );
			hillsContainer.addChild( hills2 );
			
			stage.addChild( hillsContainer );
			stage.addChild( roadContainer );
			addEventListener( Event.ENTER_FRAME, onEnterFrame );
		}
		
		public function onEnterFrame( evt:Event ):void
		{
			car.x = car.x + 10;
			roadContainer.x = 275 - car.x;
			if ( road.x + roadBreadth + roadContainer.x < 0 )
			{
				road.x = road.x + (2 * roadBreadth);
			}
			if ( road2.x + roadBreadth + roadContainer.x < 0 )
			{
				road2.x = road2.x + (2 * roadBreadth);
			}
			
			hillsContainer.x = 275 - car.x;
			if ( hills.x + hillsBreadth + hillsContainer.x < 0 )
			{
				hills.x = hills.x + (2 * hillsBreadth);
			}
			if ( hills2.x + hillsBreadth + hillsContainer.x < 0 )
			{
				hills2.x = hills2.x + (2 * hillsBreadth);
			}
		}
	}
}

(The new lines are 12-15, 24-26, 35-38, 45-47, 49, and 67-75. How did you do?)

Here is the result:

You might be wondering why I've bothered creating a hillsContainer. If so, then nicely spotted! We could just addChild() the hills to the roadContainer - but creating a new container for the background is what lets us create the actual parallax effect.

Step 24: The Actual Parallax Effect

The effect only requires changing one line of code:

hillsContainer.x = 275 - car.x;

into this:

hillsContainer.x = (275 - car.x) * 1/5;

This makes the hills scroll at 1/5th the speed of the road and car.

It looks like this:

You don't have to use 1/5; make this value larger or smaller until the speed feels right to you.

Why does this work? Well, remember that we see things in a cone of vision; the further away something is the more of it we can see. So if we walk past two objects of the same size, but one is further away, the closer of the two will appear to move faster, like so:

Let's add another background layer, even further away than the hills.

Step 25: Create Mountains

This is exactly the same as creating the road and the hills, so I'm not even going to paste the code this time! All I'll do is post a picture of my mountains..

Blue moutains

..tell you that my mountains' breadth is 751.5, x is 0 and y is 63.0; remind you to create a new mountainContainer MovieClip; and let you know that my mountains scroll at 1/16th the speed of my road.

Oh, and show you the result:

Step 26: Create the Sky

The sky is nice and easy. Since it's really far away, it scrolls so slowly that it looks like it's barely scrolling at all. Clouds and birds move, of course, and the Sun rises and sets, but none of this is due to any parallax scrolling effect. This means we don't have to make anything in the sky scroll!

(The exception to this is if the camera is travelling really, really fast - like, the speed of an aeroplane or rocket. Even then, be sure to make it scroll very slowly.)

So, no need to worry about breadth here, or creating an infinitely repeating image. It's still a good idea to make a skyContainer, though, just to keep things consistent. My sky is just a blue rectangle:

Sky so blue

If you place it at x=0, y=0 it'll cover the whole stage. Here's what it looks like in the SWF:

Step 27: Create a Big Tree in the Foreground

We've created a lot of background objects, but nothing closer to the camera than the car. As I'm sure you realise, such an object would have to scroll faster than the roadContainer, so let's try this.

For my foreground object, I've drawn a tree:

Big tree

The tree's a little different from the other objects we've made so far because it's not made to loop - it stands alone, it won't join to another tree standing next to it. This means that we only ever need one tree on screen at any one time (especially since it's so big!)

So we only need one Tree object in the code as well. Write the code for this object. If you're using my graphics, the starting x-position will be 780.0 and the y-position will be 175.0.

Since the tree will scroll, we still need a treeContainer, and we still need a treeBreadth. However, this time, the treeBreadth just controls the number of pixels between each tree. I've used a nice round 1000.0 for mine.

Step 28: Scroll the Tree

Since there's only one tree, the scrolling code is much simpler:

treeContainer.x = (275 - car.x) * 3;
if ( tree.x + treeBreadth + treeContainer.x < 0 )
{
	tree.x = tree.x + (2 * treeBreadth);
}

Nothing complicated :) Just note that it scrolls three times faster than the road. Here is the final result:

Congratulations! You've created a dynamically scrolling camera, infinitely repeating backgrounds, and a pseudo-3D parallax effect :)

Further Ideas to Try

Here are few more things you can do with the same code:

If you're creating a shoot-'em-up and you'd like all your explosions to appear closer to the camera than your enemies, simply create a new explosionsContainer, addChild() any explosions to that, and make it scroll at the same speed as the enemiesContainer.

Put the player's score, their lives counter, the mute and pause buttons, and any other parts of your game's interface into a single container. Place this container in front of all other containers, but don't make it scroll. This is an easy way to keep a game's camera and assets separate from its interface.

Try having one container stay still while making the containers in front of it and behind it scroll in opposite directions. This creates a cool rotation effect, as seen about five minutes into this clip from Disney's Snow White!

Conclusion

Thanks for reading this tutorial; I hope you enjoyed it. If anything was unclear at all, or if you'd like to ask any questions about the effect, please post a comment below.

Speaking of comments, if you create anything using this tutorial, I'd love it if you posted a link so I could see it :)


Enjoy this Post?

We'd love your vote!

User Comments

( ADD YOURS )
  1. PG

    André October 21st

    This code is very clear and very instructive, congratulations for your tutorial, and thanks a lot

    ( Reply )
  2. PG

    Robin October 21st

    Thanks a lot for your tutorial! I’m planning on making my own game for the iPhone when Flash CS5 hits and can’t wait to put this to good use.

    ( Reply )
  3. PG

    MichaelJW October 21st

    Thanks, André and Robin, I’m glad you found it useful :)

    ( Reply )
  4. PG

    Maurizio Liberato October 21st

    Thanks Michael for the tutorial! very useful! Anything like having the parallax effect on mouse hover? something like http://activeden.net/item/parallax-manager/302

    Thx :)

    ( Reply )
  5. PG

    I'M ALIVE October 21st

    nice tut, very clear coding! thank you…….

    ( Reply )
  6. PG

    Dario Gutierrez October 21st

    Fantastic Michael!! I will dev a flash game and apply this tips. Thanks.

    ( Reply )
  7. PG

    Porter October 21st

    Pretty in depth tutorial, many can benefit from this. I don’t think there should ever be a platformer game that doesn’t include this, it makes the game look substantially better. My newest game already has this included, it looks amazing due to some more complex additions we did to the the background and foreground. Awesome tutorial, thanks for sharing.

    ( Reply )
  8. PG

    MichaelJW October 21st

    Thanks guys :)

    @Maurizio: yes, they work off the same principle. If you want to do a similar mouse follow with this tutorial, try this:

    At the start of your onEnterFrame() function, put this:

    var focusX:Number = ((mouseX/stage.stageWidth)+0.5)*stage.stageWidth;

    Then, replace:

    roadContainer.x = 275 – car.x;

    …with:

    roadContainer.x = 275 – focusX;

    Do the same for your other containers, like so:

    hillsContainer.x = (275 – focusX) * 1/5;

    You’ll probably want to tweak it more than that, but that’s a pretty good start :)

    @Dario: Great!

    ( Reply )
    1. PG

      André October 21st

      Excelent, good to see people with post helping here!!

      ( Reply )
      1. PG

        MichaelJW October 21st

        Oh neat, I didn’t realise you could do nested comments.

  9. PG

    eli mcmakin October 21st

    fantastic work. Keep it up.

    ( Reply )
  10. PG

    Web 2.0 October 22nd

    A very detailed tutorial, and very useful… Great work.

    ( Reply )
  11. PG

    Phil October 22nd

    Im glad you didnt spend too much time on the graphics. It looks pretty bad in the end but it’s all about the technique on this one. Nicely done.

    ( Reply )
    1. PG

      MichaelJW October 31st

      Thanks Phil. Yeah, I always find it off-putting when a coding tutorial seems to require amazing art skills to follow, so I try to make mine as simple as possible.

      (Though I don’t claim that I could actually make better graphics than these!)

      ( Reply )
  12. PG

    Daniel Correia October 22nd

    Big thanks!! This tutorial came exactly when I needed. Going to get A+ at school :)

    ( Reply )
  13. PG

    HybridMind October 22nd

    Great tutorial! Thanks for writing this up. It is always good to see how people solve these 2d staples like viewports and parallax scrolling. Long live 2d games! ;)

    ( Reply )
    1. PG

      MichaelJW October 31st

      Ahhh, “viewports”! Thanks, I couldn’t remember the right word :)

      ( Reply )
  14. PG

    David Moreen October 22nd

    That is interesting.

    ( Reply )
  15. PG

    Leo October 23rd

    i love your tutorial ! THanks you so much!

    ( Reply )
  16. PG

    andrew October 23rd

    very great indeed. workflow is amazing, I have been spending too much time on the design side, need to get back into coding…this is a good start.

    BTW, the snow white clip reminded me, has anyone noticed the double standard for Disney films? Snow White takes an apple from an old lady and passes out, the “beast” from beauty and the beast doesn’t help an old lady and he gets transformed…damned if you do damned if you don’t

    ( Reply )
    1. PG

      MichaelJW October 31st

      Cheers Andrew.

      Haha, nice point. I’d never noticed that. Guess Disney found old ladies suspicious :P

      ( Reply )
  17. PG

    bob synclair October 24th

    r.e.s.p.e.c.t

    ( Reply )
  18. PG

    Flash Framer October 25th

    Very cool tutorial. Thanks

    ( Reply )
  19. PG

    some rescue guy October 28th

    hillsContainer.x = (275 – car.x) * 1/5;

    I would show this, but then change it to

    hillsContainer.x = (275 – car.x) * 0.2;

    Using exact values to increase cpu performance.

    ( Reply )
  20. PG

    some rescue guy October 28th

    Thanks a bunch for your help in the past year. The game is close to demo status. Also intresting idea (new approach) to making the camera appear to follow MC. I myself find it common to use a movieclips x&y in calculations.

    ( Reply )
    1. PG

      MichaelJW October 31st

      Thank you rescue86k :)

      You’re right, using 0.2 instead of 1/5 would be slightly faster. I find 1/5 easier to understand at a glance, though.

      How does your approach of making the camera follow the MC differ?

      ( Reply )
      1. PG

        some rescue guy November 14th

        My approach can be a bit confusing. But basically changing the documents x and y to follow the MC. Through doing this it causes severe lag(moving all objects including the playscreen itself), which is why I spent so much time optimizing code, graphics, and sound. It wasn’t until I did “something” with the game mechanics itself that made things fan out smooth…

        sorry can’t share this part :(

  21. PG

    yos October 29th

    very good tutorial
    thx u very much

    ( Reply )
  22. PG

    rob November 9th

    Michael,

    This is a GREAT tutorial; thanks for showing us how this works! This same effect can be easily done with motion tweens to avoid all that coding, but it’s nice to know how to use code to move things along a timeline. I wouldn’t have had a clue of how to create this effect with code, so it’s nice to see how this works up close!

    Cheers!
    Rob

    ( Reply )
    1. PG

      MichaelJW November 9th

      Hey, thanks Rob!

      Yeah, that’s a good point about tweens. Even if you wanted to use code-controlled motion, I guess it would be much easier to test out the overall speeds of the layers using tweens in the IDE.

      Glad you found it useful :)

      ( Reply )
  23. PG

    sjaak November 10th

    Very nice tutorial, very clear to follow and some of the techniques might come in handy some day :) thx alot!

    ( Reply )
  24. PG

    S December 3rd

    What if you wanted to add more than one road? for instance road 1,2,3,4, how would the code look in order for them to scroll properly?

    ( Reply )
    1. PG

      MichaelJW December 4th

      Hey S,

      First, start by having two different roads that are exactly the same width (and breadth) — let’s call them RoadOne and RoadTwo. Then, instead of having:

      public var road:Road;
      public var road2:Road;
      //…
      road = new Road();
      road2 = new Road();

      …you would have:

      public var road:RoadOne;
      public var road2:RoadTwo;
      //…
      road = new RoadOne();
      road2 = new RoadTwo();

      If you do this, it’ll work immediately, with no need to change anything else.

      Now, to add a third road, you set up a new var called road3 of the symbol RoadThree (let’s say), and then change your scrolling code from this:

      if ( road.x + roadBreadth + roadContainer.x < 0 )
      {
      road.x = road.x + (2 * roadBreadth);
      }
      if ( road2.x + roadBreadth + roadContainer.x < 0 )
      {
      road2.x = road2.x + (2 * roadBreadth);
      }

      …to this:

      if ( road.x + roadBreadth + roadContainer.x < 0 )
      {
      road.x = road.x + (3 * roadBreadth);
      }
      if ( road2.x + roadBreadth + roadContainer.x < 0 )
      {
      road2.x = road2.x + (3 * roadBreadth);
      }
      if ( road3.x + roadBreadth + roadContainer.x < 0 )
      {
      road3.x = road3.x + (3 * roadBreadth);
      }

      Note that not only have we added a new if-statement, but we've also changed "(2 * roadBreadth)" to "(3 * roadBreadth)" each time.

      You can then repeat that to use as many different sections of road as you like, as long as they have the same breadth.

      For roads with different breadths, you'll need to make several different roadBreadth variables, so the above code ends up looking like this:

      if ( road.x + road1Breadth + roadContainer.x < 0 )
      {
      road.x = road.x + (road1Breadth + road2Breadth + road3Breadth);
      }
      if ( road2.x + road2Breadth + roadContainer.x < 0 )
      {
      road2.x = road2.x + (road1Breadth + road2Breadth + road3Breadth);
      }
      if ( road3.x + road3Breadth + roadContainer.x < 0 )
      {
      road3.x = road3.x + (road1Breadth + road2Breadth + road3Breadth);
      }

      Make sense? Hope that helps :)

      ( Reply )
  25. PG

    trollbringer December 8th

    how do you stop this on a specific frame?

    ( Reply )
    1. PG

      MichaelJW December 12th

      You could remove the ENTER_FRAME listener:

      removeEventListener( Event.ENTER_FRAME, onEnterFrame );

      …though you’ll also want to call:

      car.stop();

      …if you want the car to stop animating.

      ( Reply )
  26. PG

    qFox December 27th

    Thanks. I’ve replicated your demo in Javascript only :)
    http://qfox.nl/notes/75 and http://car.qfox.nl

    ( Reply )
    1. PG

      MichaelJW January 1st

      Awesome, thanks for sharing :)

      Heh, seems to run faster in JS than AS.

      ( Reply )
  27. PG

    Nirbhay December 27th

    Very clearly explained. Thank You.

    ( Reply )
    1. PG

      MichaelJW January 1st

      Cheers!

      ( Reply )
  28. PG

    bob January 5th

    thanks for this tutorial

    ( Reply )
  29. PG

    Aayush January 9th

    This is a very nice tutorial and I’ll definitely appreciate you on that account.
    But its very lengthy. It can’t be more simpler?…..
    Also, In the beginning steps (2, 3 and 4) :- Do we have to make the pictures first and then convert them into symbols or we have to create the symbols first and then make the pictures in it?…….

    ( Reply )
    1. PG

      MichaelJW January 16th

      Cheers Aayush :)

      Yeah, I guess it is lengthy… the final code isn’t that long though, it’s the explanation more than anything.

      It’s up to you whether you make the pictures first or the symbols first — it doesn’t matter as long as you end up with some symbols that have images inside them.

      ( Reply )
  30. PG

    John Smith January 12th

    Hi Michael,

    Really enjoyed the tutorial. Top-class intro to a very cool old-skool technique. It’s amazing how simple this technique is and yet how effective and uber it looks. Using Flash’s built-in support for animation you can do wondrous things with this.

    Just a couple of things I noticed:

    1. Adding fractional numbers continuously in the way used above (i.e. treeContainer.x = (275 – car.x) * 1/5) will eventually result in floating point precision errors that could lead to “cracks” appearing. This will only happen under specific circumstances. The easiest one to prove off the top of my head would be if this system was used in a game where the player could move back & forward at will with the car. Over time discrepancies would build up in the numbers due to accumulated floating point inaccuracies being multiplied against yet more floats!

    The solution to this is to use the base position of objects and then compute a TOTAL offset from the origin of the container. I won’t go into too much detail here, but suffice to say that you would need to store a “base” x & y pos for each parallax object and then add the car’s position as a large offset, multiplied by the parallax factor (1/5 in this example). This would prevent floating point inaccuracies from piling up (multiply is particularly demonic for this type of bug)

    2. The code used to determine whether objects need to be “moved” across could easily have been turned into a function taking the “breadth”, x pos and sprite and re-used. The code would have been a lot shorter and easier to understand. However I understand that you are not trying to create the greatest parallax system EVA so that is perfectly fine and the way you have introduced the concept will allow users who read the tutorial to most likely reach the same conclusion and optimize it themselves.

    All up, very good work, I enjoyed the tutorial and now I’m going to mess around with this stuff for a game of my own :)

    ( Reply )
    1. PG

      MichaelJW January 16th

      Thanks John!

      1. Good point, and something I hadn’t considered. Though, do you mean the line “tree.x = tree.x + (2 * treeBreadth)” rather than “treeContainer.x = (275 – car.x) * 1/5)”?

      I guess the simplest way to get around this would be to use the treeContainer’s x-position as the base point that you mentioned, then, yes, add on some multiple of the car’s position. Nice solution.

      2. Great idea! I do like to repeat myself in tutorials in order to hammer certain points home, but you’re right, this could become a great re-usable system. Perhaps you could pass the distance (from the camera), x-pos, and then a list of sprites and their corresponding breadths to the function, so that you could make a tiled background instead of just repeating the same sprite.

      Cheers for your comment :)

      ( Reply )
  1. Arrow
    Gravatar

    Your Name
    January 16th