Search code examples
androidandroid-canvasside-scroller

Drawing and scrolling paths performance issues


Hi I'm doing and endless sidescroller game where the terrain looks like a tunnel which is infinite. I managed to randomly generate the tunnel using this code:

private void createPaths() {    
    if(startingPath) {
        pathBottom.setLastPoint(0, canvasHeight);
        pathTop.setLastPoint(0, 0);

        slopeWidth = 0;
        slopeHeight = generateRandomNumber(canvasHeight / 4, canvasHeight / 2);

        lastX = 0;
        lastY = canvasHeight - slopeHeight;
        newX = lastX;
        newY = lastY;

        startingPath = false;
    } else {
        lastX = canvasWidth;
        lastY = newY;
        newX = lastX;
        newY = canvasHeight - slopeHeight;
    }

    pathBottom.lineTo(lastX, lastY);
    pathTop.lineTo(lastX, lastY - OFFSET);

    do { 
        lastX = newX;
        lastY = newY;

        slopeWidth = generateRandomNumber(canvasWidth / 8, canvasWidth / 2);
        newX += slopeWidth;

        if(i % 2 == 0) {
            slopeHeight = generateRandomNumber(canvasHeight / 12, canvasHeight / 6);
            newY = canvasHeight - slopeHeight;
        } else {
            slopeHeight = generateRandomNumber(canvasHeight / 4, canvasHeight / 2);
            newY = canvasHeight - slopeHeight;
        }

        pathBottom.cubicTo(
                interpolateLinear(lastX, newX, 0.333f),
                lastY,
                interpolateLinear(lastX, newX, 0.666f),
                newY,
                newX,
                newY);
        pathTop.cubicTo(
                interpolateLinear(lastX, newX, 0.333f),
                lastY - OFFSET,
                interpolateLinear(lastX, newX, 0.666f),
                newY - OFFSET,
                newX,
                newY - OFFSET);
        i++;
    } while (newX < canvasWidth * 2);
    pathBottom.lineTo(newX, canvasHeight);
    pathTop.lineTo(newX, 0);
}

and scroll it using:

public void updateTerrain() {
    moveX -= speed;
    int pos = newX - canvasWidth + moveX;
    if(pos > 0) {
        Matrix matrix = new Matrix();
        matrix.setTranslate(-speed, 0);
        pathBottom.transform(matrix);
        pathTop.transform(matrix);
    } else {
        createPaths();
        moveX = 0;
    }
}

The problem is: the longer the path is the game becomes more "choppy". I think I should reduce the points that are being draw in the path after some time but to be honest I have no idea how to do it and still let the terrain scroll and generate. I would be grateful if you could help me. Thanks.


Solution

  • This looks like a small piece of a larger piece of logic. The performance issue may lie in some other code not shown here.

    The general advice ( according to people like Romain Guy and Chet Haase ) is to avoid object allocation ( aka new ) during onDraw. Any "new" has the potential to trigger GC.

    I would re-use the same instance of Matrix and just update it.

    Also, as fadden mentioned "fixed-size sliding window structure" ( similar to circular buffer or ring buffer ) the comment above, you should ensure that your Path objects are a fixed size.

    1. Pick a fixed number of points for the path ( let's say 200 )
    2. Keep track of those points in an array and keep a "startindex" variable to track of the "logical" start of the array. When you need to add a new point, increment the index modulo the array size, overwrite the last point ( index - 1 modulo array size ). When you get to the end of the array you have to wrap ( start back at the beginning and go to startindex - 1 ).
    3. Use path.incReserve to preallocate the memory when the view is created.
    4. Use path.rewind to reset the path
    5. Then re-use the same Path instance, to re-add all your points from your array of points ( starting at the "startIndex" )