Reputation: 133
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).
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
Reputation: 25943
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 0°
and green by 120°
with yellow in between at 60°
with smooth transitions in terms of hue:
We will fix a saturation of 100%
and a lightness of 50%
but you can play with these values if you like.
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:
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
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
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
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;
}
}
Upvotes: 4