Tutorial 07: Working with Colors

CNA Tutorial Series  ·  Beginner

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:

NameRGBUse for
Color::CornflowerBlue100149237Default XNA clear color
Color::White255255255Neutral tint (no tinting)
Color::Black000Dark backgrounds
Color::Red25500Enemies, health
Color::Green01280Success, health bar
Color::Blue00255Water, mana
Color::Yellow2552550Score, coins
Color::Orange2551650Fire, warmth
Color::Purple1280128Magic, poison
Color::Transparent000Fully transparent (A=0)
Color::TransparentBlack000Same as Transparent
Color::MonoGameOrange231600CNA 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.