Aaron Kübler
Aaron Kübler

Reputation: 133

Trying to convert Integer range to RGB color

I know you guys are all very busy, so I'm gonna keep it short and to the point.

I'm currently developing a little game for fun. There's enemys in the game. For simplicity, think about them as colored squares. I want to minimize any HUD, so I decided to display the hit points of the creatures smoothly via their color (green for healthy, yellow for damaged, red for nearly dead).

See image : enter image description here

I however really struggle to come up with a decently efficient method that converts an (int) hp-value to an RGB-color. Mapping to a single int from 0-255 would not be a problem at all - here's an example function that does exactly that:

public int mapHpToGreyscale(int input) {

    //input = current hp
    double minHealth = 0;
    double maxHealth = hpmax;
    double minColValue = 0;
    double maxColValue = 255;

    int output = (int) ((input - minHealth) / (maxHealth - minHealth) * (maxColValue - minColValue) + minColValue);

    return output;
}

Is there a quick and easy way to achieve what I want to do? I'd appreciate any input.

Upvotes: 10

Views: 8103

Answers (4)

Zabuzard
Zabuzard

Reputation: 25943

Explanation

First off, my answer is an adapted version of this: Calculate color values from green to red. I decided to create a Java version of that JavaScript solution. Code which you can directly plug-in into your code without transferring everything yourself.

The idea is to use the HSL (hue, saturation, lightness) color space instead of RGB (red, green, blue). There red is represented by the hue value and green by 120° with yellow in between at 60° with smooth transitions in terms of hue:

Hue color circle HSL color space

We will fix a saturation of 100% and a lightness of 50% but you can play with these values if you like.


Usage

Here is how you would use it in your code:

// A value between 1.0 (green) to 0.0 (red)
double percentage = ...
// Get the color (120° is green, 0° is red)
Color color = transitionOfHueRange(percentage, 120, 0);

And that would be the resulting range:

Hue range example


Other methods

Here is the transitionOfHueRange method. It accepts a percentage value between 0.0 and 1.0 and a hue range between 0 and 360:

public static Color transitionOfHueRange(double percentage, int startHue, int endHue) {
    // From 'startHue' 'percentage'-many to 'endHue'
    // Finally map from [0°, 360°] -> [0, 1.0] by dividing
    double hue = ((percentage * (endHue - startHue)) + startHue) / 360;

    double saturation = 1.0;
    double lightness = 0.5;

    // Get the color
    return hslColorToRgb(hue, saturation, lightness);
}

Here is the hslColorToRgb function. It accepts HSL values from 0.0 to 1.0:

public static Color hslColorToRgb(double hue, double saturation, double lightness) {
    if (saturation == 0.0) {
        // The color is achromatic (has no color)
        // Thus use its lightness for a grey-scale color
        int grey = percToColor(lightness);
        return new Color(grey, grey, grey);
    }

    double q;
    if (lightness < 0.5) {
        q = lightness * (1 + saturation);
    } else {
        q = lightness + saturation - lightness * saturation;
    }
    double p = 2 * lightness - q;

    double oneThird = 1.0 / 3;
    double red = percToColor(hueToRgb(p, q, hue + oneThird));
    double green = percToColor(hueToRgb(p, q, hue));
    double blue = percToColor(hueToRgb(p, q, hue - oneThird));

    return new Color(red, green, blue);
}

The hueToRgb method:

public static double hueToRgb(double p, double q, double t) {
    if (t < 0) {
        t += 1;
    }
    if (t > 1) {
        t -= 1;
    }

    if (t < 1.0 / 6) {
        return p + (q - p) * 6 * t;
    }
    if (t < 1.0 / 2) {
        return q;
    }
    if (t < 2.0 / 3) {
        return p + (q - p) * (2.0 / 3 - t) * 6;
    }
    return p;
}

And finally the small utility method percToColor:

public static int percToColor(double percentage) {
    return Math.round(percentage * 255);
}

Upvotes: 10

tevemadar
tevemadar

Reputation: 13225

Is there a quick and easy way to achieve what I want to do?

Yes, there is, you could even reuse your own code, just those constants should be arguments instead:

public int mapHpToComponent(int input,
    double minHealth, double maxHealth,
    double minComponentValue, double maxComponentValue) {

    int output = (int) ((input - minHealth) / (maxHealth - minHealth) *
        (maxComponentValue - minComponentValue) + minComponentValue);

    return output;
}

Then apply it for your palette-segments. There are two of them, one running from HP=0...50, and having G=0...255, then the second segment is HP=50...100 and R=255...0:

public int mapHpToColor(int input){
    if(input<50){ // first segment
        int R=255;
        int G=mapHpToComponent(input,0,50,0,255);
        int B=0;
        return (R<<16)+(G<<8)+B;
    } else {
        int R=mapHpToComponent(input,50,100,255,0);
        int G=255;
        int B=0;
        return (R<<16)+(G<<8)+B;
    }
}

(Yes, min/maxComponentValue could rather be start/end, because that is what they really are.)

Upvotes: 2

Ardavast Dayleryan
Ardavast Dayleryan

Reputation: 31

import java.lang.Math;

public class Color {
    public int r;
    public int g;
    public int b;
    public Color() {
        r = 0;
        g = 0;
        b = 0;
    }
};

public static Color hpToColor(float hp, float maxhp) {
    Color color = new Color();
    float alpha = hp / maxhp;

    if (alpha <= 0.5) {
        color.r = 255;
        color.g = Math.round((alpha * 2) * 255);
        color.b = 0;
    } else {
        color.r = Math.round(((1 - alpha) * 2) * 255);
        color.g = 255;
        color.b = 0;
    }

    return color;
}

Upvotes: 2

Arnaud
Arnaud

Reputation: 17534

This answer is based on the algorithm of one of the answers from Algorithm: How do I fade from Red to Green via Yellow using RGB values? that someone mentioned in a comment.

Note that minHealth and minColValue are not used in this example, but it shouldn't be too hard to implement.

import java.awt.Color;
import java.awt.GridLayout;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class HealthColors {

    static double minHealth = 0;// unused here
    static double maxHealth = 100;
    static double minColValue = 0;// unused here
    static double maxColValue = 255;

    public static void main(final String[] args) {

        JFrame frame = new JFrame();

        JPanel content = new JPanel();

        content.setLayout(new GridLayout(10, 10, 2, 2));

        for (int i = 0; i < maxHealth; i++) {

            int value = (int) (Math.random() * maxHealth + 1);

            JLabel label = new JLabel("" + value, SwingConstants.CENTER);
            label.setOpaque(true);
            label.setBackground(mapHpToColor(value));

            content.add(label);

        }

        frame.setContentPane(content);
        frame.pack();
        frame.setVisible(true);

    }

    public static Color mapHpToColor(final int input) {

        //input = current hp

        double redValue = (input > maxHealth / 2 ? 1 - 2 * (input - maxHealth / 2) / maxHealth : 1.0) * maxColValue;
        double greenValue = (input > maxHealth / 2 ? 1.0 : 2 * input / maxHealth) * maxColValue;
        double blueValue = 0;

        Color hpColor = new Color((int) redValue, (int) greenValue, (int) blueValue);

        return hpColor;
    }

}

enter image description here

Upvotes: 4

Related Questions