Monday, July 4, 2011

DDTe3 Hours 2 to 4 Drawing the Puzzle

As my Mother is still in the hospital, I am quite thankful that I have been writing my development articles as the games were written so I am months ahead of schedule when it comes to material that is ready to post. If only I could remember to post things on time :). This series was interrupted by a couple of weeks to celebrate the release of Duke Nukem Forever and to take a brief look at my ImageAtlas library but now we continue by developing the core classes that will allow me to create a lot of games really quickly.

The BasePuzzleView class has the job of displaying the puzzle. This puzzle is drawn from an image that is passed into this class. To make the game as flexible as possible, I want to be able to use only part of an image. This makes it easier to work with images that only have a partially usable portion. More important, at least in my situation, it makes it possible to maintain the proper aspect ratio for an image that is a different size than the canvas the game is going to be played in. While it is certainly possible to make the canvas a proportional size to the image being used, I like to keep my canvas a constant size. As my BGLayers library already has a rectangle class, it is simple enough to pass in a clipping region. Finally, we need the model class being displayed. With this information we have the constructor for our class.

When I started to write this class, two ways of implementing it came immediately to my mind. One way would be to have the class do all the bitmap manipulation itself when drawing. The other would be to have the class made up of a bunch of child ImageLayers that hold all the pieces of the puzzle. The first method removes a lot of overhead from having a lot of layers on the display list at a cost of a very difficult rendering process. While more efficient, speed probably is not going to be an issue whatsoever with any of the games that make up this project, so the easier to implement child path is the one I decided to go with.

The first step I then went on to implement was determining if borders were necessary. My old-school thinking was that tiles should be integers which means that it is possible for there to be borders. I was not thinking that the view is a layer that uses logical coordinates and is going to be scaled and that partial pixels are already handled by BGLayers. This is a weird lack of thinking as I am taking advantage of how BGLayers uses logical coordinates by setting the size of the layer to the size of the clipping region. Still, this is a fairly simple problem to solve. The tile size is determined by dividing width and height of the clipped image by the number of columns and rows of the puzzle. This is rounded down so the borders width is simply the width of the image clip minus the width of the tiles multiplied by the number of columns in the puzzle. Likewise the height of the borders is simply the height of the image clip minus the hight of the tiles multiplied by the number of rows in the puzzle.

The size of the individual borders is half the size of the border. If the border is an odd size, one border needs to be a pixel bigger than the other border for whatever orientation the border represents (top/bottom or left/right). The easiest way of doing this is to make the first border the total size of the borders divided by two rounded down and the other border being equal to the size of both the borders minus the size of the first border. This way of dealing with splitting odd integers is a handy trick to remember if you have to deal with integers a lot. Using integers was something that use to be important to graphics programming (when floating point numbers were thought to be evil due to how slow non-hardware accelerated floating point was) but is not so much the case anymore.

Once the borders have been created, it is very easy to lay out the tiles as the following code segment clearly shows.

   indx = 0;
   for (cntrR = 0; cntrR < puzzle.puzzleHeight; ++cntrR) {
       for (cntrC = 0; cntrC < puzzle.puzzleWidth; ++cntrC) {
           this.tiles[indx] = new BGLayers.ImageLayer("tile"+indx, img);
           rect = new BGLayers.Rectangle(left + this.tileWidth * cntrC,
                   top + this.tileHeight * cntrR,
                   this.tileWidth, this.tileHeight);
           this.tiles[indx].setClip(rect);
           this.addChild(this.tiles[indx], rect);
           ++indx;
       }
   }

The only problem is that the tiles are laid out in the proper order, not in the order reflected in the current state of the puzzle data. This problem had me stumped for a second or two, but then I realized that all I needed to do was move the tiles to the position they are supposed to be in. As this is something that happens every time the puzzle data changes, an update function was created to deal with this task.

PuzzleGallery.BasePuzzleView.prototype.update = function()
{
   var cntrR, cntrC, indx, tile;
  
   var w = this.puzzle.puzzleWidth;
   var h = this.puzzle.puzzleHeight;
  
   indx = 0;
   for (cntrR = 0; cntrR < h; ++cntrR) {
       for (cntrC = 0; cntrC < w; ++cntrC) {
           tile = this.puzzle.getPuzzlePiece(cntrC, cntrR);
           this.tiles[tile].moveTo( this.tileLeft + cntrC * this.tileWidth,
                   this.tileTop + cntrR * this.tileHeight);
           this.tileBorders[indx].setCorrect(tile == indx);
           ++indx;
       }
   }
   this.addDirty(null);
}

The best part is that to make the tiles animate to their new position, I can simply create an animateTo function within the BGLayers library and have all the work done for me. I don't know if I will have time to do this within the 24 hours alloted to this project, but it is certainly something that I want to add to the BGLayers library eventually.

Now that we have the puzzle being displayable, we need to get on with creating a game. That, as you have already guessed, will be covered next week.

No comments: