Side-Scrolling Graphics

In this demonstration, we set aside API drawing in order to return to bitmap graphics, to show a simple technique for creating the basis of a side-scrolling video game.

I. Architecture

The main movie for this project, sceneScroller.fla, contains two layers, each with an instance of a Movieclip called cell_mc, which is linked to the class file cellClass.as. There is also a document class file, scrollerDocClass.as

The two instances of cell_mc do not have instance names. One of them is positioned on the main movie stage at 0,0. the other is positioned at -650,0 (that is, entirely off-screen to the left).

Included in the working file set is a folder called gfx, in which there are 10 JPEG graphics. These graphics all measure 650x350 pixels, and depict views of a simple, cartoonish landscape. The right and left edges of each JEPG (that is, the horizon line of the landscape) match perfectly.

II. Document class

Let's start with the document class, whose package contents look like this:

import flash.display.MovieClip;
import flash.net.URLRequest;
	
public class scrollerDocClass extends MovieClip
{
  public var randy:int = 0;
  public var randyOld:int = 0;
  public var theReq:URLRequest;
		
  function scrollerDocClass()
  {}
		
  function getReq():URLRequest
  {
    while(randy == randyOld)
    {
      randy = Math.floor(Math.random()*10);
    }
    randyOld = randy;
    theReq = new URLRequest("gfx/img_"+randy+".jpg");
    return theReq;
  }
  	
}

These are the standard imports for a class that needs to work with the URLRequest class (i.e., one that wants to help load JPEG files).

All three public variables should also look familiar: the first two handle constrained random numbers (no repetitions in two selections), and the third contains that URLRequest.

The Constructor for this class is blank (yes, that's allowed).

There is one custom method: getReq(), which as you can see, generates a random number, checks to make sure it hasn't been picked on the previous run of the method (using a technique now well familiar), then plugs the random number into an URLRequest, which it returns.

So why do we do this in the document class, and not in the class file attached to our two cell clips? Answer: because we need to coordinate image selection between the two cells! That is, both cells must choose different JPEGs to display. We can only ensure that this happens by storing the value of a prior selection (by either cell) at the root level, where it is unique.

In fact, we could have put the getReq() method into the cell class, referring the recording variable randyOld at the root. The design choice here is essentially arbitrary; but since we haven't done much with methods defined in the document class, we thought we'd try one out here.

III. cellClass

The main business of the project happens in the class file for the twin instances of cell_mc. Package contents of that class are as follows:

import flash.display.MovieClip;
import flash.display.Loader;
import flash.net.URLRequest;
import flash.events.Event;
	
public class cellClass extends MovieClip
{
  public var theLoader:Loader = new Loader();
		
  function cellClass()
  {
    getImage();
    addEventListener(Event.ENTER_FRAME,animate);
  }
  
  function getImage():void
  {
    theLoader.load(MovieClip(root).getReq());
    addChild(theLoader);
  }
		
  function animate(evet:Event):void
  {
    x +=8;
    if(x > 642)
    {
      getImage();
      x = -650;
    }
  }
		
}

There's nothing unfamiliar about the imports: standard fare for a class that will bring in graphics with Loader, working with an enter-frame callback.

There is one declared variable, giving a name to our instance of Loader.

In the Constructor, we peform the getImage() method, which brings in an initial JPEG graphic, and which we'll discuss in detail directly. We also set up the listener for enter-frame action, handing off to the animate() method.

Now for getImage(). This method performs load() on myLoader -- but note how it derives its URLRequest: as a call to Movieclip(root).getReq(). Since our root-level method getReq() is one of those functions that returns a value (in this case, a URLRequest object), we can plug in the method call right where we would otherwise plant a variable or expression. Again, remember that we have a reason (however arbitrary) for putting the getReq() method at the root.

Finally, we come to the animate() method. It's pretty simple: after adding 8 (pixels) to the current value of x -- which advances the current MovieClip 8 pixels to the right of the screen -- it checks to see if that value has exceeded a trigger point. If it has, our MovieClip is repositioned at -650, offscreen left.

So this class file tells each of our two scenery cells to slide to the right until one of them runs off the left edge, at which point that one flies around and lines up offstage left.

However, you may be curious about that trigger point. Since each of our graphics (or cells) is 650 pixels wide, you might expect the trigger to be 650. Yet we use 642. Why?

Answer: because both cells are in motion, we need to account for motion by the other cell whenever one cell tests against the limit. That is, when cell A reaches the limit, it does so before its partner has made its 8-pixel advance. Accordingly, we need to jump early, giving the other guy a chance to slide in and fill our gap.

If you're not convinced of this logic (and we are, only because we've spent years teaching this code example), go ahead and change the trigger point to 650. you'll notice a visible gap when your cells recycle. If you look closely, you'll see that gap is exaclt 8 pixels. Change the trigger value back, and the gap will disappear.

Behold, the mysteries of software.

IV. Source files

All files for this demonstration can be found in the sideScroller directory, within MULTIMEDIA in the shared account on student-iat.



University of Baltimore Logo

Last updated: 06/24/08 16:26:21
Copyright © 2008 School of Information Arts and Technologies