1
\$\begingroup\$

I just added an option to my game that makes it so that all the enemies will have a random color for their clothes instead of the regular blue. I use this code to draw rectangles over the shirt of the enemy.

BufferedImage image = new BufferedImage(z.getWidth(), z.getHeight(), 
    BufferedImage.TYPE_4BYTE_ABGR);
try{
    image = ImageIO.read(new File("C:\\Program Files (x86)\\ZombieGame\\
        Zombie.png"));
}catch (IOException ex) {
}
int j, k, red, green, blue;
    for(j = 0; j < z.getWidth(); j++){
        for(k = 0; k < z.getHeight(); k++){
            Color c = new Color(image.getRGB(j, k));
            red = c.getRed();
            green = c.getGreen();
            blue = c.getBlue();
            if(red == 0 && green == 0 && blue == 178){
                g2d.setColor(Color.red);
                Rectangle r = new Rectangle(j,k, 1, 1);
                Path2D.Double rect = new Path2D.Double(r, at);
                g2d.fill(rect);
            }
        }
    }

The thing is that when I apply this the game will run twice as slow (before I added this code I got a solid 80 fps but as fast as I added it I got around 30 fps). Is there a better way in which I could change the color of specific parts of the image without lowering the fps drastically?

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Unrelated to the question, but hardcoding C:\Program Files (x86)\ZombieGame is a bad idea. \$\endgroup\$
    – msell
    Commented May 21, 2013 at 15:26

4 Answers 4

2
\$\begingroup\$

You would probably be better off having your artists produce variations on the default color set, and decide which one to use when the zombie is initially created. The implementation you are using now performs per texel checks to only change a specific color, and say your image is 100x100, that is 10,000 color checks per zombie, on top of the final draw calls. This is a ton of run time overhead just for changing a color. Instead, you could simply select a random texture when the zombie is initially created using a single call to a random function.

\$\endgroup\$
0
\$\begingroup\$

Evan seems correct. It may also be possible to divide the zombies into two textures; one consisting of any parts of them not likely to change color (ie, skin) simply floating in midair, and another for parts of them that may change tint (ie, shirt). You could draw these on top of each other, and specify a tint color only for the second texture in the SpriteBatch call.

I still feel like there may be a better option; I'm fairly sure there was some innovative approach to this in RTS's like Age of Empires and Starcraft (in which each side's units have bits of a certain color on them).

\$\endgroup\$
0
\$\begingroup\$

You could reserve a certain color value in your image to be the "tint color", something you are unlikely to actually use like (0, 255, 0). Then create a function that copies the image and loops through all the pixels, every time you find the tint color replace it with some other color using setRGB().

Then for each zombie variant, create a new variation of the image:

BufferedImage redZombie = tintImage(image, red);
BufferedImage greenZombie = tintImage(image, green);

If you were using openGL or DirectX I would have recommended just doing it in a pixel shader.

\$\endgroup\$
1
  • \$\begingroup\$ I'd be concerned about doing this in a pixel shader IFF it involved a test and branch - I have a pathological dread of stalls :-) \$\endgroup\$ Commented May 22, 2013 at 1:00
0
\$\begingroup\$

Evan is right if you have the artist bandwidth, otherwise, you could make this algorithm a lot more efficient. The issue you have is that you're twiddling individual bits in about the most expensive possible way you could.

Right now, you're testing each pixel individually to see if it's blue and changing it if it is - however, you said that you're drawing rectangles

A) Consider making a Boolean, say IsBlueEnemy that indicates that the enemy instance is in fact blue - or you could make this a color property with value 0,0,178, eg EnemyColorProperty

B) If the enemies are all of the same size, then either define constants that give the X and Y origin and the width and height of the rectangle, or define a static rectangle that you reuse (I did the former to illustrate, but the latter is preferable)

C) Now fill the whole rectangle in one fell swoop (note I am not defining g2d and at since you didn't either :-) )

 if(IsBlueEnemy)
 {
     g2d.setColor(Color.red);
     Rectangle r = new Rectangle(RECT_ORIGIN_X,RECT_ORIGIN_Y, COLORWIDTH,COLORHEIGHT);
     Path2D.Double rect = new Path2D.Double(r, at);
     g2d.fill(rect);
     IsBlueEnemy = false;
}

If you don't really have a solid rectangle, i.e. there are occlusions you don't want to paint, then you need to dig into the image and get a hold of the raw bits and manipulate those - the bottom line is that the routines you're using are very expensive and are really only useful for messing with a few bits at a time if you're concerned with performance

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .