Libgdx change color of Texture at runtime
Asked Answered
G

3

9

In a game made with libgdx I have a TextureAtlas in which I have stored all the TextureRegions for my Animations of the Player. The Player by default has a blue T-Shirt (for example). Now I would like to be able to have more then one Player and each should have another T-Shirt color. So basically, I want to replace the blue with red for the second Player and with green for the 3rd Player and so on. I am sure I can do this with PixMap but wouldn't. Because then I lose the advantage of the TextureAtlas(?). Is there another way to do this? Or do I need to have every "color version" as a TextureRegion in the TextureAtlas?

Another little question:
With Gimp (and maybe a few other programs) you can use color indexes for ".gif" files. This reduces the size of all your Textures by saving an index for every color in the file and then using this index to describe the pixels. So for every red pixel you would have a "1" instead of "#FF0000" and somewhere in the file you have a "1=#FF0000". If we then pack the ".gif" files with the color indexes inside a TextureAtlas, is the index then lost and it restores the default RGB colors or will that make problems?

Thanks a lot!

Graecoroman answered 4/6, 2014 at 9:56 Comment(4)
Just make the T-Shirt texture white and then use spriteBatch.setColor(1, 0, 0, 1) before rendering the red player for example.Bungalow
I always do this with some Sprite and Sprite#setColor(Color tint) :)Drislane
@Bungalow but if i do that, the whole TextureRegion is affected or am i wrong? Will it only affect the white part?Graecoroman
@Nizil this will be the same as spriteBatch.setColor(1, 0, 0, 1), but only for Sprites, i have TextureRegions. Thanks anyways (:Graecoroman
M
5

I faced the same Issue for generating weapon with random colors using the same texture.

So I wrote this.
Basically I make a pixmap of the texture you want to edit.

Then you iterate over all of the pixels, while iterating I check for certain colors which are a part specific part of the texture. (I suggest using different shades of gray since the RGB is the same)

Then when it is on a pixel where the color needs to be changed I grab a color for those pixel groups using a color picker method which is basically random which gets a color from a prefabbed color array,
and then changes that specific pixel to the new color.

/**
 * Requires a asset's textureName, and requires gray scale colors of the
 * parts
 * 
 * @param texturename
 * @param colorBlade
 * @param colorEdge
 * @param colorAffinity
 * @param colorGrip
 * @return
 */
private static Texture genTexture(String texturename, int colorBlade,
        int colorEdge, int colorAffinity, int colorGrip, int colorExtra) {
    Texture tex = Game.res.getTexture(texturename);

    TextureData textureData = tex.getTextureData();
    textureData.prepare();

    Color tintBlade = chooseColor(mainColors);
    Color tintEdge = new Color(tintBlade.r + 0.1f, tintBlade.g + 0.1f,
            tintBlade.b + 0.1f, 1);

    Color tintAffinity = chooseColor(affinityColors);
    Color tintGrip;
    Color tintExtra = chooseColor(extraColors);

    boolean colorsAreSet = false;

    do {
        tintGrip = chooseColor(mainColors);

        if (tintAffinity != tintBlade && tintAffinity != tintGrip
                && tintGrip != tintBlade) {
            colorsAreSet = true;
        }
    } while (!colorsAreSet);

    Pixmap pixmap = tex.getTextureData().consumePixmap();

    for (int y = 0; y < pixmap.getHeight(); y++) {
        for (int x = 0; x < pixmap.getWidth(); x++) {

            Color color = new Color();
            Color.rgba8888ToColor(color, pixmap.getPixel(x, y));
            int colorInt[] = getColorFromHex(color);

            if (colorInt[0] == colorBlade && colorInt[1] == colorBlade
                    && colorInt[2] == colorBlade) {
                pixmap.setColor(tintBlade);
                pixmap.fillRectangle(x, y, 1, 1);
            } else if (colorInt[0] == colorEdge && colorInt[1] == colorEdge
                    && colorInt[2] == colorEdge) {
                pixmap.setColor(tintEdge);
                pixmap.fillRectangle(x, y, 1, 1);
            } else if (colorInt[0] == colorAffinity
                    && colorInt[1] == colorAffinity
                    && colorInt[2] == colorAffinity) {
                pixmap.setColor(tintAffinity);
                pixmap.fillRectangle(x, y, 1, 1);
            } else if (colorInt[0] == colorGrip && colorInt[1] == colorGrip
                    && colorInt[2] == colorGrip) {
                pixmap.setColor(tintGrip);
                pixmap.fillRectangle(x, y, 1, 1);
            }
            else if (colorInt[0] == colorExtra && colorInt[1] == colorExtra
                && colorInt[2] == colorExtra) {
            pixmap.setColor(tintExtra);
            pixmap.fillRectangle(x, y, 1, 1);
            }
        }
    }

    tex = new Texture(pixmap);
    textureData.disposePixmap();
    pixmap.dispose();

    return tex;
}

I hope this helps.
Please don't just copy paste, try to rebuild this to suit your needs or you won't learn anything.

Maduro answered 6/6, 2014 at 8:55 Comment(3)
Hi Rose Blax and thanks for this solution. It is basicly what i thought about to do. But the question here is: Does this remove the advantage of the TextureAtlas, as you create a new Texture or does it stil take the TextureAtlas?Graecoroman
I would say try and figure it out, I haven't tried so I have no idea.Maduro
I would recommend calling new Color() only once outside of the two for loops then setting the rgba values in that spot instead. It's computationally expensive to create instances, so avoid doing so when it's unnecessary.Extravasate
P
4

A way of making this is just setting a maximum number of players, and make T-shirts for everyone, and pack them with texture atlas and add them into one TextureRegion array.

Now you would only have to switch between indexes.

Another way to do it is with batch.setColor

void    setColor(Color tint)
void    setColor(float color) 
void    setColor(float r, float g, float b, float a) 

And just set the color of the sprite batch for every T-Shirt you draw, and after you drawn all of them, put the spritebatch color to white again.

You can also do this with the Sprite class with the setColor function.

If your T-Shirts doesn't have a very big size, and your maximum player number is a small one, I would recommend the first method.

Pga answered 4/6, 2014 at 11:26 Comment(5)
How do you come up with the number 15? And why prefer the first method? Seems like worse option to me. Changing the color basically yields infinite amounts of different shirts and you only need to maintain the texture of a single one.Bungalow
It was just my opinion, a game probably won't have more than 15 players, anyways I've edited it. I would use this method because is the easiest way, and for small t-shirts and players number as I've said, is easy to just assign an index for every player..Pga
Thanks for the answer. There wouldn't be a problem, if there is only 1 T-Shirt for every color, but the Animation of the player will contain some more Textures, so this is not really an option, or at least the last one i want to use... The setColor() is interesting, but wouldn't it affect the whole Texture?Graecoroman
It will affect the whole texture, but black will stay black and white will change to the colour of the batch. You should not use any other colours because those will get mixed and will look weird.Bungalow
@Bungalow But the final result should be a Player (think about a human) with a colored T-Shirt. So this is not possible with the setColor method?Graecoroman
S
2

Rose Blax did really a good work. But if you want a way to do that by using SpriteBatch or for someone who has same issue but does not want to use Pixmap...

For sure I really like to use Pixmap, but I have seen the usage of "spritebatch/sprite.setcolor(r,g,b,a)" gave some challenges as change all texture color instead only the t-shirt... That's why I came here and yes, it's possible to change only the t-shirt using this way. Are you ready? :D

1 - Make the part want to paint grey scale or white 2 - Split the parts would paint in the texturepack or a different texture: in case of a t-shirt, you need a pack where the t-shirt is out of your char... Yes, will be someone with head, arms and legs only. The t-short may be in the same pack or not. But keep a hole between then to put the t-shirt in the future.

it will look like that:

Check out this asset

Do not need to be scared. I know that image has splited whole characther, but If you want to change only the t-shirt then you will split only it from rest of body.

3 - You will draw they both, but not same TextureRegion, your code will look like that:

public void draw(SpriteBatch batch){
        //First we draw the player with a hole in his body
        batch.draw(playerA,0,0,75,100);
        //Here we set the color of next texture to be drawn as RED.
        batch.setColor(Color.RED);
        //Now we draw the t-shirt of the player in same x and half y.
        //That t-short has to be drawn in right position. That's why calc it.
        batch.draw(tshirtA, 0,50,50);
        //For don't affect others textures then make it white back.
        batch.setColor(Color.WHITE);
    }
Sharonsharona answered 3/8, 2018 at 19:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.