An Infinite, Spherical Map

sphereMap.swf

This project extends our encounter with top-view maps to a second level of sophistication: a map that scrolls without limits. To accomplish this trick, we'll take our cue directly from nature. How is it that you could (if you didn't mind getting your feet wet at some point) start walking in a straight line, anywhere on the planet Earth, and continue along that line forever? The answer, obviously, is that your route only appears to be a straight line in your local context. In fact, your path follows a circle, since that is what all straight lines become when applied to a sphere. Eventually, your circular straight-line movement would bring you back to your starting point.

In this project, we apply a virtual, spherical geometry to a grid of square cells, which will allow us to navigate around that grid of cells endlessly and seamlessly. In the example above, sphereMap, you may change the view by moving your mouse into any edge or corner. Watch the numbered cells shift as you do this. Soon enough you'll see Cell 5, your starting point, come around again. Diagonal movement is supported as well as lateral.

I. Architecture

This project is more complicated than the finite map, but still fairly modest. In the main movie, sphereMap.fla, there is linkage to a document class, sphereMapDocClass.

The Library contains one MovieClip, called cell_mc. This MovieClip has two layers, one containing a simple gray fill, the other a text field with the instance name readout. This field displays the cell's number. The MovieClip cell_mc is linked to a class called cellClass.

We'll look at this class file first.

II. Cell class

The imports are standard: MovieClip, flash.text.TextField, and flash.events.Event

After that, the class is fairly compact:

public class cellClass extends MovieClip
{
  //CONSTRUCTOR
  public function cellClass()
  {
    addEventListener(Event.ENTER_FRAME, rePlace)
  }
		
  //CUSTOM METHODS
  public function rePlace(e:Event)
  {
    if(MovieClip(root).Xnav == "right") x -=10;
    if(MovieClip(root).Xnav == "left") x +=10;
    if(MovieClip(root).Ynav == "down") y -=10;
    if(MovieClip(root).Ynav == "up") y +=10;
			
    if(x <= -800) x = 400;
    if(x >= 800) x = -400;
    if(y <= -800) y = 400;
    if(y >= 800) y = -400;
  }
}

The Constructor simply establishes an enter-frame callback called rePlace(). This method, in turn, resembles the nav() method in our finite-map project (which may be a bit confusing, since we'll have a slightly different method called navigate() in our document class here).

The rePlace() method moves the cell according to the values of two variables, Xnav and Ynav, defined and updated at the root. The quartet of if tests following the movement statements perform the most crucial operation in this project: they instruct the cell to reset its position if it exceeds one of four limits. You'll see what this accomplishes when we discuss the document class.

Before we go there, however, we need to call attention to an important design aspect of this project. Unlike in the scrollable map, we're not controlling a single object, but rather a grid or ensemble of cells -- nine, to be exact. It's important to recognize that in this code each cell functions independently. That observation may seem counter-intuitive, if you look at the behavior of the system. The grid appears to move as a single piece. In fact, this is an illusion. Each of the cells making up the grid moves in turn; but of course, those turns happen in milliseconds, far too fast for your eyes to detect.

It's also important to note what these independently-cooperating cells do when they move to their respective limit points. When a cell can no longer move any further right, it relocates itself to the left of the visible window -- and so forth for leftward, upward, and downward movement, as well.

There are only four relcation points, one for each axial limit, and this may at first seem confusing. Shouldn't a cell from the extreme left side of the grid relocate further out on the right, when it hits its limit? The answer is no, of course, because when that extreme-left cell reaches the limit point, the rest of the grid has moved left, as well. Every cell relocates to the same point, but it does so under different conditions.

Thus the whole thing is like a symphony, a choral dance, or the movement of machine parts: independent action under a general pattern or logic.

As we've seen, the moving part of that logic is largely contained in the cell class. The aspect of arrangement or configuration is handled mainly in the document class.

III. Document class

At the top of the document class file, sphereMacDocClass.as, we import MovieClip and Event, the declare three variables:

  public var theCell:MovieClip;
  public var Xnav:String;
  public var Ynav:String;

The variable theCell is an alias or holder that allows us to create and configure each of the nine instances of cell_mc from which we construct the skin of our virtual sphere.

The twin variables Xnav and Ynav allow us to pass information from mouse position, in order to control movement, or navigation.

Now we come to the Constructor, which is not as elaborate as its length might suggest:

  function sphereMapDocClass()
  {
    for(var i:int=1; i<10; i++)
    {
      theCell = new cellClass();
      theCell.readout.text = String(i);
				
      switch(i)
      {
        case 1:
          theCell.x = -400;
          theCell.y = -400;
          break;
		  
        case 2:
          theCell.x = 0;
          theCell.y = -400;
          break;
		  
        case 3:
          theCell.x = 400;
          theCell.y = -400;
          break;
		  
        case 4:
          theCell.x = -400;
          theCell.y = 0;
          break;
		  
        case 5:
          theCell.x = 0;
          theCell.y = 0;
          break;
		  
        case 6:
          theCell.x = 400;
          theCell.y = 0;
          break;
		  
        case 7:
          theCell.x = -400;
          theCell.y = 400;
          break;
		  
        case 8:
          theCell.x = 0;
          theCell.y = 400;
          break;
		  
        case 9:
          theCell.x = 400;
          theCell.y = 400;
          break;
		  
      }
				
      addChild(theCell);
    } //end construction loop
			
    addEventListener(Event.ENTER_FRAME, navigate)		
  }

This method builds the 3x3 grid of cells that forms the surface of our virtual world. You could, of course, place the cells in layers of the timeline, by hand; but that would take considerable precision, not to mention time. Document classes provide a sound alternative to this sort of fingerwork.

The main structure of this method is a for loop that runs nine iterations, with an index i that runs from 1 to 9. This index serves two purposes: it gives us the number that we display in the text field of each cell, and also lets us determine where the cell should be placed in the grid.

On each pass of the loop, we generate a new instance of cellClass (which, since it is linked, gives us a new instance of cell_mc). Then we negotiate a switch construction, which you've seen in many previous projects.

The pivot point of our switching operation is i, the index of our for loop. We use this discrminator to match each new cell with a particular set of coordinates. We arrived at these coordinates by making a sketch of the project on paper. If you're adept at things like geometry (and carpentry), you might be able to do this math in your head. In which case, you're a cleverer monkey than we.

If the numbers above confuse you, start with the number 5 case, which represents the central cell of our nine-cell grid. Note that its coordinates are 0,0. This alignment point represents the upper left corner of the cell.

All our cells are 400x400 pixels, and they are numbered in graph order, from the upper left of the grid to the lower right. So the cell immediately to the left of the central cell (cell 4) has its corner point at -400, 0: parallel to the central cell in the Y axis, but 400 pixels in the negative or righthand direction. And so on for the other 7 cells in the grid.

After instantiating and configuring a cell, we perform addChild() to put it on the Display List. There's no need to worry about removing anything, since we're only running this method (the Constructor of our document class) once.

When the for loop is complete, we add an enter-frame listener whose callback is navigate(), the method we'll use to control movement of the cells. This is the lone custom method in our document class, and the last detail we need to discuss:

  public function navigate(e:Event)
  {
    Xnav = "";
    Ynav = "";
			
    if(mouseX < 30) Xnav = "left"
    if(mouseX > 370) Xnav = "right";
    if(mouseY < 30) Ynav = "up"
    if(mouseY > 370) Ynav = "down";
  }

Here once again is that familiar quartet of if tests, though this time the consequence of a match is not direct movement, but assignment of a String value to one of our two directional variables, Xnav or Ynav.

You've already seen the other end of this transaction: depending on how these variables are set, each of the nine cells responds with some kind of movement. Since we're using distinct variables for horizontal and vertical movement, we can move diagonally in two axes at once.

Ahead of our four tests, however, we set both control variables to "" or null. Doing this allows for a possibility in which none of the four test conditions is met. In this case, the cells respond by doing nothing. We arrive at this condition whenever the cursor is in the middle of the screen, outside any of the four hot strips.

That is the whole extent of the document class, and thus of the project.

sphereMap_2.swf

IV. Variation: tiling graphics, etc.

The example at right shows a variation of the basic spherical-map project, where the simple gray fill has been replaced with a JPEG graphic specially modified for seamless tiling. You can produce such graphics yourself in Photoshop, or find an example from a stock photo CD or other source.

To suggest further possibilities, we've also added an independently moving object that bumbles or buzzes around our tiny planet, always moving just slowly enough that you can catch it and hold it in view, if you like. Here's the code for this object, contained in a class file called spheroidClass.as:

  public class spheroidClass extends MovieClip
  {
    //CONSTRUCTOR
    function spheroidClass()
    {
      addEventListener(Event.ENTER_FRAME, rePlace);
      alpha = 0.5;
    }
	
    public function rePlace(e:Event)
    {
      rotation +=4;
      x +=7;
      if(Math.floor(Math.random()*2) == 0) y -=Math.random()*8;
      if(Math.floor(Math.random()*2) == 0) y +=Math.random()*8;
			
      scaleX = (8 + Math.random()*5)/10;
      scaleY = scaleX;
			
      //INDENTICAL TO CELL CLASS FROM THIS POINT
      if(MovieClip(root).Xnav == "left") x -=10;
      if(MovieClip(root).Xnav == "right") x +=10;
      if(MovieClip(root).Ynav == "up") y -=10;
      if(MovieClip(root).Ynav == "down") y +=10;
			
      if(x <= -800) x = 400;
      if(x >= 800) x = -400;
      if(y <= -800) y = 400;
      if(y >= 800) y = -400;
    }
  }

As you can see, about half this code is the same as in cellClass.as -- so a more efficient way to write this class would begin by extending cellClass, not MovieClip. We're more interested in clarity than efficiency, just now.

The non-common bits of this class have mainly to do with the appearance and behavior of the rambling object: its random changes of scale and direction.

V. Source files

Source files for both versions of this project are available within the sphereMap subdirectory of MULTIMEDIA on student-iat. The initial version, comprising sphereMap.fla and sphereMapDocClass.as, is within a directory called Version_1. Files for the variant project are in Version_2.



University of Baltimore Logo

Last updated: 05/04/08 17:41:29
Copyright © 2008 School of Information Arts and Technologies