0

I am developing an Android game. As the game progresses it gets steadily slower. I am sure some of the speed changes are due to the higher enemy count and stuff like that, but I am also worried that I may have a memory leak.

The nature of the game is this:

It is a tower defense game built with lists and arrays. All of the tower data is stored in numerous gameBoard arrays. Throughout the drawing and update code every block is evaluated. Most times nothing needs to be done. The game is a 30x14 grid, so 420 locations are evaluated probably 15 times throughout the course of the loop. This isn't great programming, but I assumed the code was simple enough that it would work fine. And most times nothing needs to be done, so I didn't think it would matter.

The enemies, all bullets and various particle effects are all stored and updated in lists like the code seen below. I skinned the game a couple days ago, keeping all of the game logic the same, but adding effects. Adding bitmaps and coloring styles did not change the way the game ran. The slowdown was apparent when I added particle effects like those shown below. They do not cause any slowdown early in the game. Later in the game they do. I assumed that the memory was being managed wrong because of this.

I have a bunch of different classes like this:

public class FadeBlock {
    private float x;
    private float y;
    private float radius;
    private int level;
    private float startRadius;
    private float endRadius;
    private float elapsedTime;
    private float fadeTime;
    private boolean terminate;
    private float percent;

    public FadeBlock(int Level, float CenterX, float CenterY, float StartRadius, float EndRadius, float FadeTime)
    {
        x = CenterX;
        y = CenterY;
        level = Level;
        fadeTime = FadeTime;
        terminate = false;
        percent = 1;
        startRadius = StartRadius;
        endRadius = EndRadius;
        radius = startRadius;
    }

    public void Update(float ElapsedTime)
    {
        elapsedTime += ElapsedTime;
        percent = (((float) elapsedTime) / ((float)fadeTime));
        radius = (endRadius - startRadius)*percent + startRadius;

        if (percent >= 1)
        {
            terminate = true;
        }
    }

    public void Draw(Canvas canvas, Paint paint)
    {
        if (!terminate)
        {
            switch (level) {
            case 1:
                paint.setARGB(255, 61, 190, 255);
                break;
            case 2:
                paint.setARGB(255, 162, 0, 194);
                break;
            case 3:
                paint.setARGB(255, 0, 194, 162);
                break;
            case 4:
                paint.setARGB(255, 129, 194, 0);
                break;
            case 5:
                paint.setARGB(255, 255, 85, 0);
                break;
            case 6:
                paint.setARGB(255, 194, 65, 0);
                break;
            default:
                paint.setARGB(255, 61, 190, 255);
                break;
            }
            paint.setAlpha((int) (255*(1 - percent)));

            canvas.drawRect(x-radius, y - radius, x + radius, y + radius, paint);
        }
    }

    public boolean Kill()
    {
        return terminate;
    }

}

That I add to an arrayList like this:

public List<FadeBlock> FadingBlocks = new ArrayList<FadeBlock>();

And manage with the following loops:

for (int i = 0; i < FadingBlocks.size(); i++)
{
    FadingBlocks.get(i).Draw(canvas, paint);
}

for (int i = 0; i < FadingBlocks.size(); i++)
{
    FadingBlocks.get(i).Update(elapsedTime);
}

for (int i = (FadingBlocks.size() - 1); i > -1 ; i-- )
{   
    if (FadingBlocks.get(i).Kill())
{                       
        FadingBlocks.remove(i);
    }
}

To add items to the list I do something like this:

FadeBlock tempBlock = new FadeBlock(Units.get(i).getLevel(), Units.get(i).getX()+16, Units.get(i).getY()+16, 16, 32, 250)                   
FadingBlocks.add(tempBlock);

Is that a bad way to manage my objects? Would that cause memory issues over time on an android device?

Nathan Tornquist
  • 6,468
  • 10
  • 47
  • 72
  • 1
    Don't worry that you might have a memory leak. Use a tool and make sure that you do (or don't). There's plenty of tools that you can use to check for memory leaks. In fact, a good place to start would be to check out a few of the posts listed in the right-hand column of this page. – David Wasser Jul 05 '12 at 15:09
  • Use a memory profiler. Don't guess. – Mariusz Jamro Jul 05 '12 at 15:12
  • I've been using DDMS. No memory issues are showing up in LogCat, and I don't know how to use the other tools enough to really make good use of them. It is just confusing as to why my game would slow down. There aren't really more things being drawn to the screen, and the enemies don't do a lot more at later levels. – Nathan Tornquist Jul 05 '12 at 15:17
  • @AlexLockwood That's naming conventions, some languages use PascalCasing (such as C#) and standard C# conventions include Uppercase methods/classes/properties with lowercase local variables. This is a convention used throughout the standard C# libraries and suggested standards. Carrying these over to Java through Android doesn't break anything inherently unless names begin to overlap. He should use whatever convention he prefers, not yours. – Brandon Buck Jul 05 '12 at 15:19
  • While `new` is not great practice and it is always a better idea to reuse objects, is that something that would cause my program to slow down over time? – Nathan Tornquist Jul 05 '12 at 15:22
  • @AlexLockwood Again, the naming conventions you use are dependent on, first, the preferences of who you work for, and second, yourself should they not be covered by who you work for. Yes, it's important to clarify things for other people if you plan to have them read over their code. And naming conventions are naming conventions, they are not standards. They are preference. PascalCasing can be used in Java just as easily as camelCasing can be used in C# and the same underlining_word_splits can be used outside of Ruby. – Brandon Buck Jul 05 '12 at 15:25
  • @AlexLockwood So again, you're showing me suggested naming conventions. They are "suggested" and not required by the language. – Brandon Buck Jul 05 '12 at 15:29
  • @AlexLockwood Since you like links, check the first answer here. Although the question is specific to C#, the answer is language independant. [A good response](http://stackoverflow.com/questions/149491/pascal-casing-or-camel-casing-for-c-sharp-code) – Brandon Buck Jul 05 '12 at 15:30
  • @AlexLockwood If you're working on the Android Project I'd be all for following their conventions, if you're working on Java for Oracle I'd be all over using their conventions. If they're working for you, they should use your conventions. But they don't, so whatever conventions he's supposed to be using according to them are the ones that apply, not yours, not Oracle's, and not Android's. – Brandon Buck Jul 05 '12 at 15:36
  • @AlexLockwood He's working on an Android project for himself or his company... right? He may have copy/pasted a snippet direct from his code... right? So it may not have been formatted for the people on StackOverflow, either work around it, ask him to format it a certain way, or leave. You don't approach it as "You did it wrong because you didn't do it my way." – Brandon Buck Jul 05 '12 at 15:42
  • @AlexLockwood Judging from you arguments to me you may be misunderstanding me. I'm not telling you that you are incorrect in pushing certain naming conventions. I am all for using what the Framework uses, if Android uses camelCase, use camelCase. But your approach to the problem was wrong and you claimed that it was "the actual problem" to his issue. Naming conventions are, ultimately, just conventions to try to standardize code. They are **not** standards and blindly assuming they have to be followed for code to work is erroneous. – Brandon Buck Jul 05 '12 at 15:45
  • I've added an explanation of the game. – Nathan Tornquist Jul 05 '12 at 15:51
  • @NathanTornquist I've not done any work with Android but the particle engines I've done personally in the past have used the Memory Pool method suggest in XGouchet's answer. You may want to try that and see how that improves performance of the game. Also, you may want to only test blocks on the grid that contain something that will can actually do something instead of testing all grid squares every loop. – Brandon Buck Jul 05 '12 at 15:57
  • @izuriel Thanks for trying to help. This was a strange thread. I've never had anything like that happen on SO before. – Nathan Tornquist Jul 05 '12 at 15:58
  • I didn't get offended that your comments did not help. I got offended that instead of simply saying I should use the java standard, you listed every violation of said standard and argued that my code was unreadable because it did not comply to the standard. -- The objects are removed. – Nathan Tornquist Jul 05 '12 at 16:17

1 Answers1

3

It's real hard to know if this or that causes a Memory Leak from a few lines of code. Here are a thought for you :

Use the ObjectPool pattern for your game : you create N enemies while starting your game, some of them inactive. Then when you need a new enemy, instead of creating a new one and adding it to your ArrayList, you take one from the pool which is inactive, and activate it. Samely, instead of deleting an enemy, you just mark it as inactive in the pool.

This can prevent memory overuse, as an ArrayList starts with a capacity of x, and when needed will increase to 2x etc... but it will never release memory when you remove object. Also store you object pool in a static list instead of an array list, as iterations and access to the object will be faster.

XGouchet
  • 10,002
  • 10
  • 48
  • 83