Performance Issues: CPU

I’m having a ton of issues with performance and it seems to be a CPU issue. I think I might be trying to do far too much with my game.

Its a top down RTS style game and you have 20 units. The problem is that I’m addicted to loops and arraylists. I dont know any other way to do some of the things I need to do. I am using active rendering and FSEM. My FPS is supposed to be 60. I’m going to post most of the relevant code, but it is quite long so don’t feel obligated to sift through my horrible…horrible code.

Be warned, its ugly and probably bad programming. All of this gets done 60 times a second. I’m sure that there is a much better way to do this so it doesn’t need to be done every 60 seconds. But the screen needs to move in response to the mouse going to the edge of the screen so things need to be updated constantly. Also, I have never made a habit out of commenting so seriously, DO NOT FEEL OBLIGATED TO READ MY CODE, but any help would be appreciated. Thanks to anyone who will hold my hand through this =)

private void gameRender(Graphics dbg)
    {
        counter++;
        if(counter == 61)counter = 0;
       if(dbImage == null){
            dbImage = createImage(pWidth, pHeight);
            if(dbImage == null){
                System.out.println("dbImage is null!");
                return;
            }else
                dbg = dbImage.getGraphics();
        }
        g2 = (Graphics2D)dbg;
        dbg.setColor(Color.black);
        dbg.fillRect(0, 0, pWidth, pHeight);
        //draw game elements
        //draw main screen
        screen = new Rectangle2D.Double(-x, -y, 1024, 588);
        for(int i = 0; i < 12; i++){
                for(int p = 0; p < 7; p++){
                  int tile;
                  int xMod;
                  int yMod;
                  int x1Mod;
                  int y1Mod;
                  xMod = 0;
                  yMod = 0;
                  x1Mod = x%100;
                  y1Mod = y%100;
                  if(-x > 99) xMod = -(x/100);
                  if(-y > 99) yMod = -(y/100);
                  tile = mL.getTile(xMod + i, yMod + p);
                  if(tile == 48) g2.drawImage(grass, (i*100)+x1Mod, (p*100)+y1Mod, null);
                  if(tile == 49) g2.drawImage(tree, (i*100)+x1Mod, (p*100)+y1Mod, null);
                }
        }
        
        for(int i = 0; i < 20; i++){
            if(counter == 60)combat(i);
            if(p1Units.get(i).getBox().intersects(screen)){
                if(playerNum == 2){
                    g2.setColor(Color.red);
                    g2.draw(new Ellipse2D.Double(p1Units.get(i).getBox().getX()+x+2.5, p1Units.get(i).getBox().getY()+45+y, p1Units.get(i).getBox().getWidth(),p1Units.get(i).getBox().getWidth()));
                }
                for(int p = 0; p < 5; p++){
                    if(i == selectedUnit[p]){
                        g2.setColor(Color.white);
                        g2.draw(new Ellipse2D.Double(p1Units.get(i).getBox().getX()+x+2.5, p1Units.get(i).getBox().getY()+45+y, p1Units.get(i).getBox().getWidth(),p1Units.get(i).getBox().getWidth()));                    
                    }
                }
                if(p1Units.get(i).getType() == "tank"){
                    g2.drawImage(tank.get(p1Units.get(i).getFacing()-1), (int)p1Units.get(i).getX() + x, (int)p1Units.get(i).getY() + y, null);
                }
                if(p1Units.get(i).getType() == "artillery"){
                    g2.drawImage(artillery.get(p1Units.get(i).getFacing()-1), (int)p1Units.get(i).getX() + x, (int)p1Units.get(i).getY() + y, null);
                }
            }
            if(p2Units.get(i).getBox().intersects(screen)){
                if(playerNum == 1){
                    g2.setColor(Color.red);
                    g2.draw(new Ellipse2D.Double(p2Units.get(i).getBox().getX()+x+2.5, p2Units.get(i).getBox().getY()+45+y, p2Units.get(i).getBox().getWidth(),p2Units.get(i).getBox().getWidth()));
                    
                }
                g2.drawImage(tank.get(p2Units.get(i).getFacing()), (int)p2Units.get(i).getX() + x, (int)p2Units.get(i).getY() + y, null);
                //if(playerNum == 1){
                    //g2.fill(new Rectangle2D.Double(p2Units.get(i).getX()+x+40, p2Units.get(i).getY()+y+100, 100, 10));
                    //g2.setColor(Color.green);
                   // g2.fill(new Rectangle2D.Double(p2Units.get(i).getX()+x+40, p2Units.get(i).getY()+y+100, p2Units.get(i).getHealth(), 10));
               // }
            }
            if(p1Units.get(i).getType() == "artillery" && p1Units.get(i).isFiring()){
                Point2D point = explosionHandler.drawExplosion(i, counter);
                if(point.getX() != 0 && point.getY() != 0){
                    g2.drawImage(explosion, (int)point.getX()-55+x, (int)point.getY()-48+y, null);
                }
            }
        }
        //draw border
        g2.setColor(Color.white);
        g2.drawImage(hud, 0, 588, null);
        g2.drawImage(minimap, 849, 593, null);
        for(int i = 0; i < 20; i++){
            g2.setColor(Color.blue);
            g2.fill(new Rectangle2D.Double((p1Units.get(i).getX()/22.86) + 849, (p1Units.get(i).getY()/22.86)+593, 4.37, 4.37));
            g2.setColor(Color.red);
            g2.fill(new Rectangle2D.Double((p2Units.get(i).getX()/22.86) + 849, (p2Units.get(i).getY()/22.86)+593, 4.37, 4.37));
        }
        if(selectedUnit[0] != 21){
            if(p1Units.get(selectedUnit[0]).getType() == "artillery"){
                Unit u = p1Units.get(selectedUnit[0]);
                if(u.getStance() == "mobile") g2.drawImage(mobile, 775, 598, null);
                else g2.drawImage(fire, 775, 598, null);
            }
        }
        g2.setColor(Color.yellow);
        g2.draw(new Rectangle2D.Double((849+(-x/22.86)), (593 + (-y/22.86)), 44.79, 25.94));
        //g2.fill(new Rectangle2D.Double(0, 568, 1024, 200));
        
    }
    private void combat(int index){
        int i2 = p1Units.get(index).getTheTarget();
        if(p1Units.get(index).isFiring()){
            
            if(i2 != 21){
                if(!p2Units.get(i2).isDead()){
                    if(!p1Units.get(index).isInRange(p2Units.get(p1Units.get(index).getTheTarget()).getX(), p2Units.get(p1Units.get(index).getTheTarget()).getY())){
                        p1Units.get(index).setIsFiring(false);
                        p1Units.get(index).setTarget((int)p2Units.get(i2).getX(), (int)p2Units.get(i2).getY());
                        p1Units.get(index).setIsMoving(true);
                    }
                    p2Units.get(i2).attacked(p1Units.get(index).getExplosionAttack(), p1Units.get(index).getBulletAttack());
                }else{
                    p1Units.get(index).setTarget(21);
                    p1Units.get(index).setIsFiring(false);
                }
            }
           
        }
        
        for(int i = 0; i < 20; i++){
            if(p1Units.get(index).isInRange(p2Units.get(i).getX(), p2Units.get(i).getY())){
                p1Units.get(index).setIsFiring(true);
                p1Units.get(index).setTarget(i);                   
            }
        }
        if(i2 != 21){
            if(p2Units.get(i2).isDead()){
                    p1Units.get(index).setTarget(21);
                    p1Units.get(index).setIsFiring(false);
            }
        }
    }

a) There are magic numbers.
b) == isn’t for string comparison (use equals() instead).
c) Use a profiler to identify the bottlenecks.

a) What?
b) It seems to work, is there a reason to use equals() instead?
c) How do I use a profiler?

EDIT: Okay, wiki’d magic numbers and I’m assuming you’re refering to my poorly named, non-documented variables. This was is mentioned in my post and it makes my code horribly unreadable, I know, I’m trying to fix that habit.

It seems to work, is there a reason to use equals() instead?

== only happens to work if both references point to the very same object. Just being identical isn’t enough. (So, it may stop working later on… once you moved some of the source around.)

How do I use a profiler?

Netbeans has a good one. You should be able to find lots of tutorials for that one. The JDK also ships with a very basic one (the -Xprof switch).

[…]I’m assuming you’re refering to my poorly named, non-documented variables.

No, it’s stuff like the 20 here:
for(int i = 0; i < 20; i++)

or the 21 there:
if(i2 != 21){

I see, so for good programming conventions in the future I should just use properly named constants for those?

Profiler stats incoming soon.

Profiler screenshot:

http://img392.imageshack.us/img392/1294/profileryq3.jpg

EDIT: If that is unreadable, its telling me my CPU is spending most of its time in gameRender() by a long shot.

I see, so for good programming conventions in the future I should just use properly named constants for those?

Yes. Or enums. E.g. those string comparisons could be replaced with enum comparisons like bla.getType()==Type.foo.

isInRange also seems to take some cpu. Keep in mind that you don’t need sqrt if you don’t need the exact distance.

You also don’t need to clear the screen if everything is overdrawn anyways.

You can also inspect the rendering in detail via:
-Dsun.java2d.trace=log
(try searching the forum for this)

Split the gameRender() method into a couple of smaller methods (using Extract Method refactoring), so that one method does one thing and one thing only (Single Responsibility Principle). Then the profiler can show better which part of gameRender() takes the most time.

Why aren’t you using BufferStrategy.getDrawGraphics? Are you using active rendering? If not, you should, given that performance is an issue.

First, why are you declaring the variables before setting them? Why not the following:

        //draw main screen
        screen = new Rectangle2D.Double(-x, -y, 1024, 588);
        for(int i = 0; i < 12; i++){
                for(int p = 0; p < 7; p++){
                  int xMod = 0;
                  int yMod = 0;
                  int x1Mod = x%100;
                  int y1Mod = y%100;
                  if(-x > 99) xMod = -(x/100);
                  if(-y > 99) yMod = -(y/100);
                  int tile = mL.getTile(xMod + i, yMod + p);
                  if(tile == 48) g2.drawImage(grass, (i*100)+x1Mod, (p*100)+y1Mod, null);
                  if(tile == 49) g2.drawImage(tree, (i*100)+x1Mod, (p*100)+y1Mod, null);
                }
        }

It doesn’t really matter, but the other way had more lines of code.

Second, get rid of the if statements. Instead of some kind of array “tileImages” and use this code:

g2.draw(tileImage[tile], (i*100)+x1Mod, (p*100)+y1Mod, null);

It’s the same for this code:

You should have a class for the unit type and give the class a drawTo method. Different unit types will implement this method differently, though you can probably just make all the unit types the draw an image be different instances of the same class. Then you can replace all those if statements by

p1Units.get(i).drawTo(xCoordinate, yCoordinate);

. You may need to have a drawTo method in the unit class if there’s ever a circumstance where 2 units of the same type look different.

Getting rid of the if statements might not improve performance, but it would make your code actually readable.

One person suggested splitting up your method into multiple methods for the profiler. That will probably help you zoom in on the problem, though I wouldn’t recommend it except for making it easier to profile. Splitting up one easy-to-understand method into several methods scattered around your code won’t help anyone. (Of course, the method isn’t “easy-to-understand” right now, but splitting it up won’t make it any better. Getting rid of the if statements will make it easy-to-understand.)

[quote]You should have a class for the unit type and give the class a drawTo method. Different unit types will implement this method differently, though you can probably just make all the unit types the draw an image be different instances of the same class.
[/quote]
How do I draw images outside of gameRender()?

Do I just pass my Graphics2D variable to the method and use it to draw in the method?

I’m not sure how that works if that isn’t how you do it.

And yes I am using active rendering I’m sure if it, I’m using the same animation loop for FSEM active rendering as is in Killer Game Programming in Java.

I had the dbImage buffering commented out, I’m not sure why all of a sudden it is in now.

The irony of this whole thing is that this is the second time writing this game and this time it was supposed to be more readable and CPU efficient. So much for that.

EDIT: I’ve been surfing around some of the other posts on this forum and it seems that swing and active rendering are a problem. All of this code is in a JFrame is that a problem? What else do I use if not JFrame?

You should really read a book about the basics of programming. You’ll find yourself severly constrained in making your game when you do not yet master these basic concepts of object oriented programming.

Any suggestions? I’m more adept at Java than I let on perhaps by my questions. Any books on basic program layout and programming basics would be greatly appreciated.

Yes.

That’s just part of having good modular code. Sprites (or whatever) should usually draw themselves upon demand.

If you were changing around the drawing code, it probably has nothing to do with the CPU. It may be that some of your images were getting hardware accelerated before but aren’t now for some reason. You should look up hardware image acceleration on this forum.

The code you posted isn’t especially CPU-intensive so far as I can tell. It seems probable that it’s been an issue with how your code is using the video card.

There’s nothing wrong with JFrame. Active rendering isn’t a problem; it’s a solution. Swing works just fine, but you might not have stumbled upon the magical “correct” way of doing things.

There’s alot of odd little details that have to be taken care of for things to work properly. For instance, some ways of loading images result in hardware accelerated images, while others do not.

The problem is probably elsewhere in your code, not in the code you posted. The code you posted can certainly be improved, but nothing about it suggests that it would run especially slow.

If you have a good knowledge of Java but not so much knowledge of game programming, I would suggest buying Developing Games in Java or Killer Game Programming Java (or, preferably, both). They’re a few years old now, but I don’t know of any better introductory Java game programming books. Of course, I only buy so many.

Killer Game Programming in Java can be found for free at https://fivedots.coe.psu.ac.th/~ad/jg/. Just click on each chapter and open the pdf file to read it.

EDIT: Well they are at least draft versions of the book’s chapters. Still good enough to learn from, though.