Java – Android – Draw mazes on Canvas with smooth character movement

Android – Draw mazes on Canvas with smooth character movement… here is a solution to the problem.

Android – Draw mazes on Canvas with smooth character movement

I’m currently creating a Tile-based game that basically uses two sets of boolean arrays to draw mazes to determine where each wall needs to be drawn.

I got this all working and only drew the 5 x 5 part of the maze (the whole maze size is 30 x 30). However, the problem I had was that when I moved the character, the whole screen jumped because the next part of the maze was being drawn to maintain a 5 x 5 aspect ratio. I’ve tried various different methods to try and make it run smoothly but can’t seem to do that.

Can someone suggest or guide me on some links/examples so that I can do mazes and character moves smoothly. Below is some basic code about the main for loop that currently draws the maze, and the 2 boolean arrays it references to draw the walls.

// THE CODE TO DRAW THE MAZE AND ITS WALLS
for(int i = 0; i < 5; i++) 
{
    for(int j = 0; j < 5; j++)
    {
        float x = j * totalCellWidth;
        float y = i * totalCellHeight;

        if(currentY != 0)
            indexY = i + currentY - 1;
        else
            indexY = i + currentY;

        if(currentX != 0)
            indexX = j + currentX - 1;
        else
            indexX = j + currentX;

         // Draw Verticle line (y axis)
        if (indexY < vLength && indexX < vLines[indexY].length && vLines[indexY][indexX])
        {
            RectOuterBackground.set((int)x + (int)cellWidth, (int)y, (int)x + (int)cellWidth + 15,  (int)y + (int)cellHeight + 15);
            canvas.drawBitmap(walls, null, RectOuterBackground, null);
        }
        // Draws Horizontal lines (x axis)
        if (indexY < hLength && indexX < hLines[indexY].length && hLines[indexY][indexX])
        {
            RectOuterBackground.set((int)x, (int)y + (int)cellHeight,(int)x + (int)cellWidth + 15,(int)y + (int)cellHeight + 15);
            canvas.drawBitmap(walls, null, RectOuterBackground, null);
        }
    }
}

// THE 2 BOOLEAN ARRAYS THE FORLOOP REFERENCES
boolean[][] vLines = new boolean[][]{
    {true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true },
    {true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true },
    {true ,true ,true ,false,false,false,true ,false,false,true ,true },
    {true ,true ,true ,false,false,true ,false,true ,true ,true ,true },
    {true ,true ,false,true ,false,false,true ,false,false,true ,true },
    {true ,true ,false,true ,true ,false,false,false,true ,true ,true },
    {true ,true ,true ,false,false,false,true ,true ,false,true ,true },
    {true ,true ,false,true ,false,false,true ,false,false,true ,true },
    {true ,true ,false,true ,true ,true ,true ,true ,false,true ,true },
    {true ,true ,false,false,false,true ,false,false,false,true ,true },
    {true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true },
    {true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true }
 };
boolean[][] hLines = new boolean[][]{
    {true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true },
    {true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true },
    {true ,true ,false,false,true ,true ,false,false,true ,false,true ,true },
    {true ,true ,false,false,true ,true ,false,true ,false,false,true ,true },
    {true ,true ,true ,true ,false,true ,true ,false,true ,true ,true ,true },
    {true ,true ,false,false,true ,false,true ,true ,false,false,true ,true },
    {true ,true ,false,true ,true ,true ,true ,false,true ,true ,true ,true },
    {true ,true ,true ,false,false,true ,false,false,true ,false,true ,true },
    {true ,true ,false,true ,false,false,false,true ,false,true ,true ,true },
    {true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true },
    {true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true ,true }
};

If I can draw that part of the code smoothly, I’m willing to rewrite it completely, but after trying several different variations and not getting it to go smoothly, I always have jumps.

I also tried to get it to work with Canvas Translation, by drawing the whoel maze and then scaling it so that only 5 x 5 is displayed and then simply moving the zoom section to use the translation while keeping the character in the center of the screen, where there is smooth movement. However, I couldn’t get the translation to go smoothly.

Solution

This is a complete maze class. It plots View onto coordinates viewX and viewY, which can move smoothly (as Boudjnah’s answer) or multiply by the cell dimensions.

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.RectF;

public class Maze {
    private RectF drawRect = new RectF();

    private Bitmap[] bitmaps;
    private int[][] tileType;

    private float screenWidth, screenHeight;

    /**
     * Initialize a new maze.
     * @param wallBitmap The desired bitmaps for the floors and walls
     * @param isWall The wall data array. Each true value in the array represents a wall and each false represents a gap
     * @param xCellCountOnScreen How many cells are visible on the screen on the x axis
     * @param yCellCountOnScreen How many cells are visible on the screen on the y axis
     * @param screenWidth The screen width
     * @param screenHeight The screen height
     */
    public Maze(Bitmap[] bitmaps, int[][] tileType, float xCellCountOnScreen, float yCellCountOnScreen, float screenWidth, float screenHeight){
        this.bitmaps = bitmaps;
        this.tileType = tileType;

        this.screenWidth = screenWidth;
        this.screenHeight = screenHeight;

        drawRect.set(0, 0, screenWidth / xCellCountOnScreen, screenHeight / yCellCountOnScreen);
    }

    /**
     * Get the type of the cell. x and y values are not coordinates!
     * @param x The x index of the cell
     * @param y The y index of the cell
     * @return The cell type
     */
    public int getType(int x, int y){
        if(y < tileType.length && x < tileType[y].length) return tileType[y][x];
        return 0;
    }

    public float getCellWidth(){ return drawRect.width(); }
    public float getCellHeight(){ return drawRect.height(); }

    /**
     * Draws the maze. View coordinates should have positive values.
     * @param canvas Canvas for the drawing
     * @param viewX The x coordinate of the view
     * @param viewY The y coordinate of the view
     */
    public void drawMaze(Canvas canvas, float viewX, float viewY){
        int tileX = 0;
        int tileY = 0;
        float xCoord = -viewX;
        float yCoord = -viewY;

        while(tileY < tileType.length && yCoord <= screenHeight){
            // Begin drawing a new column
            tileX = 0;
            xCoord = -viewX;

            while(tileX < tileType[tileY].length && xCoord <= screenWidth){
                // Check if the tile is not null
                if(bitmaps[tileType[tileY][tileX]] != null){

                    // This tile is not null, so check if it has to be drawn
                    if(xCoord + drawRect.width() >= 0 && yCoord + drawRect.height() >= 0){

                        // The tile actually visible to the user, so draw it
                        drawRect.offsetTo(xCoord, yCoord); // Move the rectangle to the coordinates
                        canvas.drawBitmap(bitmaps[tileType[tileY][tileX]], null, drawRect, null);
                    }
                }

                // Move to the next tile on the X axis
                tileX++;
                xCoord += drawRect.width();
            }

            // Move to the next tile on the Y axis
            tileY++;
            yCoord += drawRect.height();
        }
    }
}

To use this class, first initialize it in your onCreate function like this

// 0 == floor, 1 == wall, 2 == different looking wall
int[][] maze = {
        {0, 0, 0, 0, 0},
        {0, 1, 2, 1, 0},
        {0, 0, 0, 1, 0},
        {1, 1, 2, 1, 0},
        {0, 0, 0, 0, 0}
};

Bitmap[] bitmaps = {
        BitmapFactory.decodeResource(getResources(), R.drawable.floor),
        BitmapFactory.decodeResource(getResources(), R.drawable.firstwall)
        BitmapFactory.decodeResource(getResources(), R.drawable.secondwall)
};

// Chance the 480 and 320 to match the screen size of your device
maze = new Maze(bitmaps, maze, 5, 5, 480, 320);

Then plot it in your onDraw or similar function

maze.draw(canvas, viewX, viewY);

The getType(int x, int y), getCellWidth(), and getCellHeight() functions should help you with any interaction with the maze. You can also add your own functions to the class. You can also modify this class to record any beginning and end of the graph block. I won’t make the whole game for you 🙂

If you set the role to coordinates (x, y) and draw it to coordinates (x – viewX, y – viewY), you can have a fixed position relative to the maze, regardless of where the View is located.

Hope this helps!

Related Problems and Solutions