开发者

Android : Displaying score as a string on canvas is creating a new string per draw command. How do I get around this?

I'm making a game that displays some numbers on a canvas开发者_C百科 (score, time, etc).

The way that I currently do this is with the drawtext command on a canvas

// score is some int
draw(Canvas c) {
    c.drawText(score+"", x, y, paintSyle);
}

I hear that object creation and garbage collection are expensive operations, and I think this is creating a new string every time it is called.

Right now my game with all bitmap drawing and everything jumps around from 25 to 60 fps. I'd like it to stay closer to the higher number and I'm trying to find ways to speed it up.

Would it be faster/better to make(or find?) some mutable subclass of string and work around this problem? Is there another way to solve this issue? Or is this just how it is?


Introduce two new private member variables String renderedScoreString and int rederedScore and rewrite your draw()-method like that:

draw(Canvas c) {
    if (this.score != this.renderedScore || this.renderedScoreString == null) {
        this.renderedScore = this.score;
        this.renderedScoreString = Integer.toString(this.renderedScore);
    }
    c.drawText(this.renderedScore, x, y, paintStyle);
}

that should save you a lot! of object creations. You could also hide the boilerplate code behind a getter method, e.g. String getScoreString() which does the same, so you don't have it in the draw()-method.


A friend of mine tipped me in on a solution to this problem. When you want to draw something over time, one of the best (and simplest) mechanisms to do so is to split up what you need to do into two completely separate processes.

ie. Only use the draw command exclusively for drawing stuff, keep logic/assignment in Draw() to an absolute minimum.

private final long TIMER_PERIOD = 500;
private String timeString;
private Runnable updateRunnable;
private Handler updateHandler = new Handler();

public void onCreate(Bundle savedInstanceState) {
    updateRunnable = new Runnable() {
        @Override
        public void run() {
            timeString = GetTimeString();
            updateHandler.postDelayed(updateRunnable, TIMER_PERIOD);
        }
    }
}

Draw(Canvas c) {
    c.drawText(timeString, x, y, paintStyle);
}

In this example the Draw command simply takes timeString in its current state and draws it to the screen. This is highly efficient use of the draw function as it does not require any object creation, and no logic is present that is not immediately required for any drawing to occur. . In the background a Runnable is executing the run() function every 500 miliseconds (approximately). Simply update the Run() function with whatever logic you need to calculate the time (example has a dummy function GetTimeString())

I hope this is helpful.


I know I'm resurrecting a dead thread, but there is one extra optimisation you can add to this which restricts String creation to a one-time thing and thus only triggers the GC once at the start and not during the game (which is quite important for an android game).

Somewhere during the start of your game (onCreate, onResume, as part of a singleton during application startup, etc) create a large String[] which can hold the maximum score (my game fills an array of 10000, so the max score would be 9999). Then loop over it with a for loop, filling each index with a String.valueOf(i).

for (int i = 0; i <scoreStrings.length; i++)
        {
            scoreStrings[i] = String.valueOf(i);
        }

Now, when you need to draw the score, just use the int you use to store the score in as an index to that array, and "hey, presto!", you get the correct string for your score.

canvas.drawText(scoreStrings[score], x, y, paint);
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜