Tutorial 07: Working with Colors
The Color Struct
Color is a 32-bit RGBA value stored as four unsigned bytes: R (red), G (green), B (blue), A (alpha). It lives in the Microsoft::Xna::Framework namespace and is included via:
#include "Microsoft/Xna/Framework/Color.hpp"
using namespace Microsoft::Xna::Framework;
Each channel ranges from 0 (none) to 255 (full). Alpha 255 means fully opaque; alpha 0 means fully transparent.
You can read individual channels:
Color c(255, 128, 0, 200);
bytecs r = c.getR(); // 255
bytecs g = c.getG(); // 128
bytecs b = c.getB(); // 0
bytecs a = c.getA(); // 200
bytecs is the sharp-runtime equivalent of C# byte (unsigned 8-bit integer). It is implicitly convertible to and from unsigned char.
You can also access a packed 32-bit integer representation:
uint32_t packed = c.getPackedValue(); // ABGR packed
Predefined Colors
CNA provides all 141 named colors from the XNA 4.0 API as static constants on the Color class. A selection:
| Name | R | G | B | Use for |
|---|---|---|---|---|
Color::CornflowerBlue | 100 | 149 | 237 | Default XNA clear color |
Color::White | 255 | 255 | 255 | Neutral tint (no tinting) |
Color::Black | 0 | 0 | 0 | Dark backgrounds |
Color::Red | 255 | 0 | 0 | Enemies, health |
Color::Green | 0 | 128 | 0 | Success, health bar |
Color::Blue | 0 | 0 | 255 | Water, mana |
Color::Yellow | 255 | 255 | 0 | Score, coins |
Color::Orange | 255 | 165 | 0 | Fire, warmth |
Color::Purple | 128 | 0 | 128 | Magic, poison |
Color::Transparent | 0 | 0 | 0 | Fully transparent (A=0) |
Color::TransparentBlack | 0 | 0 | 0 | Same as Transparent |
Color::MonoGameOrange | 231 | 60 | 0 | CNA accent color |
// Use predefined colors directly
device.Clear(Color::CornflowerBlue);
spriteBatch_->Draw(*pixel_, Rectangle(0,0,100,100), Color::Red);
spriteBatch_->Draw(*pixel_, Rectangle(0,0,100,100), Color::Yellow);
RGBA Construction
Color has several constructors:
// From R, G, B (alpha defaults to 255 — fully opaque)
Color orange(255, 128, 0);
// From R, G, B, A
Color semiBlue(0, 0, 255, 128); // 50% transparent blue
// From normalized floats [0.0f, 1.0f] — note the f suffix
Color warm(1.0f, 0.5f, 0.0f, 1.0f); // same as (255,127,0,255)
// Copy and modify alpha
Color c = Color::Red;
Color fadeRed = Color(c, 128); // Red at 50% alpha
The float constructor is useful when you compute colors algorithmically, for example when using sine waves for pulsing effects:
float t = static_cast<float>(gameTime.getTotalGameTime().getTotalSeconds());
float pulse = (std::sin(t * 3.0f) + 1.0f) * 0.5f; // 0..1
Color glowColor(pulse, 0.2f, 1.0f - pulse, 1.0f); // blue-to-red glow
Color Arithmetic
CNA's Color supports several static utility methods for combining colors:
// Multiply two colors channel-by-channel (values clamped to [0,255])
// Used for tinting: multiplying by White gives the original color.
// Multiplying by a dark color darkens.
Color tinted = Color::Multiply(Color::Red, 0.5f); // dark red
// Add two colors (saturates at 255) — useful for additive blending
Color bright = Color::Add(Color::Red, Color(50, 50, 0, 0));
// Subtract (clamps at 0) — rare but available
Color dim = Color::Subtract(Color::White, Color(100, 0, 0, 0));
The most common arithmetic operation is multiplying by a scalar to darken or brighten:
// Darken a color to 60% brightness
Color halfBright = Color::Multiply(Color::CornflowerBlue, 0.6f);
Lerping Between Colors
Linear interpolation between two colors creates smooth transitions — fade-outs, damage flashes, day/night cycles.
// Color::Lerp(from, to, amount)
// amount = 0.0 returns 'from', amount = 1.0 returns 'to'
Color dawn = Color::CornflowerBlue;
Color dusk = Color(30, 10, 60, 255); // deep purple
// In Update():
float t = (std::sin(totalTime * 0.5f) + 1.0f) * 0.5f; // 0..1
currentSkyColor_ = Color::Lerp(dawn, dusk, t);
// In Draw():
device.Clear(currentSkyColor_);
Lerp is also useful for health bar color transitions:
// Red at 0% health, green at 100%
float healthFraction = playerHealth_ / maxHealth_;
Color barColor = Color::Lerp(Color::Red, Color::Green, healthFraction);
Transparency and Alpha
The alpha channel controls how transparent a color is. Alpha = 255 is fully opaque; alpha = 0 is fully invisible. Values in between give partial transparency.
Alpha blending in SpriteBatch is enabled by default using BlendState::AlphaBlend. You do not need to do anything special — just set the alpha of your color and it will blend with what is underneath.
// A tooltip background with semi-transparent dark panel
Color tooltipBg(0, 0, 0, 180); // 70% opaque black
spriteBatch_->Draw(*pixel_, tooltipRect, tooltipBg);
// Fade text: reduce alpha as time passes
float fadeAlpha = 1.0f - elapsedFadeTime_ / fadeOutDuration_;
Color textColor = Color(255, 255, 255, static_cast<bytecs>(fadeAlpha * 255));
To create a color that is just a copy of another with a different alpha:
Color original = Color::CornflowerBlue;
Color withAlpha = Color(original, 128); // same RGB, alpha = 128
Using Color with SpriteBatch
Every SpriteBatch::Draw and DrawString call accepts a color parameter. This color is a tint: the texture pixels are multiplied by it channel by channel.
Color::White— no tint, draw the texture as-is.- Any other color — tints the texture. A red texture tinted with blue will become very dark.
- Use the tint alpha to fade the entire sprite in or out.
// No tint — draw texture in original colors
spriteBatch_->Draw(*playerTex_, position_, Color::White);
// Damage flash: tint red
if (isHurt_) {
spriteBatch_->Draw(*playerTex_, position_, Color::Red);
} else {
spriteBatch_->Draw(*playerTex_, position_, Color::White);
}
// Ghost effect: semi-transparent
Color ghost(255, 255, 255, 100);
spriteBatch_->Draw(*playerTex_, ghostPosition_, ghost);
// Fade-in at game start
float alpha = std::min(1.0f, elapsedTime_ / fadeInDuration_);
Color fadeColor(255, 255, 255, static_cast<bytecs>(alpha * 255));
spriteBatch_->Draw(*titleTex_, titlePos_, fadeColor);
Colors are one of the most versatile tools in 2D game development. In Tutorial 08 you will load real image files and draw them with full control over position, scale, rotation, and color.