Implementing the game environment

Field class

This class is a TiledLayer that draws the grassy field and water. The water tiles are animated so that they constantly change a little, simulating waves. Its tile image is shown in the figure below.

Figure 54: Field tile image, 160 × 16 pixels

  1. Create the Field class file.

  2. Import the required classes.

    // unnamed package
    import javax.microedition.lcdui.*;
    import javax.microedition.lcdui.game.*;
    
  3. Set Field to extend TiledLayer. A TiledLayer is a visual element composed of a grid of cells that can be filled with a set of tile images. This class allows large virtual layers to be created without the need for an extremely large Image.

    For more information, see TiledLayer in the MIDP 2.0 API specification.

    class Field
        extends TiledLayer
    {
    

    Create the background field for the game.

        private static final int WIDTH_IN_TILES = 12;
        private static final int HEIGHT_IN_TILES = 12;
        private static final int TILE_WIDTH = 16;
        private static final int TILE_HEIGHT = 16;
    
        private static int[][] cellTiles =
            {{-3, -2, -3, -1, -2, -1, -3, -1, -2, -3, -1, -2},
             {-2,  3,  4,  3,  1,  2,  3,  2,  1,  5,  2, -3},
             {-1,  2,  1,  2,  3,  4,  5,  3,  2,  4,  3, -1},
             {-2,  1,  4,  9,  9,  9,  9,  4,  5,  2,  1, -2},
             {-3,  3,  5,  9, 10, 10, 10,  2,  1,  3,  5, -1},
             {-2,  2,  3,  9, 10, 10, 10,  5,  4,  2,  1, -3},
             {-1,  4,  2,  9,  9,  9,  9,  3,  1,  3,  2, -2},
             {-3,  2,  5,  1,  3,  1,  4,  2,  5,  4,  3, -3},
             {-2,  1,  4,  2,  5,  2,  3,  4,  2,  1,  2, -1},
             {-1,  5,  1,  4,  3,  4,  1,  2,  3,  4,  1, -2},
             {-3,  2,  4,  5,  2,  3,  2,  4,  1,  2,  3, -3},
             {-2, -3, -2, -1, -2, -1, -3, -2, -1, -3, -1, -2}};
        private static int FOLD_TILE = 10;
        private static int FENCE_TILE = 9;
        private static int[][] waterFrames = {{6, 7, 8}, {7, 8, 6}, {8, 6, 7}};
    
        private int tickCount = 0;
    
    
  4. Create a method for creating the field. The createAnimatedTile method creates a new animated tile and returns the index that refers to the new animated tile. The setCell method sets the contents of a cell.

    For more information, see createAnimatedTile and setCell in the MIDP 2.0 API specification.

        Field()
        {
            super(WIDTH_IN_TILES,
                  HEIGHT_IN_TILES,
                  SheepdogMIDlet.createImage("/field.png"),
                  TILE_WIDTH,
                  TILE_HEIGHT);
            createAnimatedTile(waterFrames[0][0]);      // tile -1
            createAnimatedTile(waterFrames[1][0]);      // tile -2
            createAnimatedTile(waterFrames[2][0]);      // tile -3
            for (int row = 0; row < HEIGHT_IN_TILES; ++row)
            {
                for (int column = 0; column < WIDTH_IN_TILES; ++column)
                {
                    setCell(column, row, cellTiles[row][column]);
                }
            }
        }
    
    
  5. Write the methods for determining the starting position of the dog and a method for determining the tick rate.

        int getSheepdogStartX()
        {
            return getWidth() - 50;
        }
    
        int getSheepdogStartY()
        {
            return getHeight() - 50;
        }
    
        void tick()
        {
            int tickState = (tickCount++ >> 3);   // slow down x8
            int tile = tickState % 3;
            setAnimatedTile(-1 - tile, waterFrames[tile][(tickState % 9) / 3]);
        }
    
    
  6. Create a method for checking passage into impassable areas.

        // return true if any part of the rectangle overlaps a water tile
        // or the fence
        boolean containsImpassableArea(int x, int y, int width, int height)
        {
            int rowMin = y / TILE_HEIGHT;
            int rowMax = (y + height - 1) / TILE_HEIGHT;
            int columnMin = x / TILE_WIDTH;
            int columnMax = (x + width - 1) / TILE_WIDTH;
            for (int row = rowMin; row <= rowMax; ++row)
            {
                for (int column = columnMin; column <= columnMax; ++column)
                {
                    int cell = getCell(column, row);
                    if ((cell < 0) || (cell == FENCE_TILE))
                    {
                        return true;
                    }
                }
            }
            return false;
        }
  7. Create a method for detecting if a sheep has entered the fold.

    // returns true if every pixel of the sprite is in the fold
    boolean inFold(Sprite s)
    {
        // we can assume that the sprite's reference pixel is unchanged
        int rowMin = s.getY() / TILE_HEIGHT;
        int rowMax = (s.getY() + s.getHeight() - 1) / TILE_HEIGHT;
        int columnMin = s.getX() / TILE_WIDTH;
        int columnMax = (s.getX() + s.getWidth() - 1) / TILE_WIDTH;
        for (int row = rowMin; row <= rowMax; ++row)
        {
            for (int column = columnMin; column <= columnMax; ++column)
            {
                if (getCell(column, row) != FOLD_TILE)
                {
                    return false;
                }
            }
        }
        return true;
    }
}