Tutorial 08: Loading and Drawing Textures
Texture2D Overview
Texture2D represents a 2D image stored on the GPU. In CNA you load image files (PNG, JPEG, BMP) and use SpriteBatch::Draw() to render them. Key properties:
#include "Microsoft/Xna/Framework/Graphics/Texture2D.hpp"
// After loading:
int w = texture->getWidth(); // width in pixels
int h = texture->getHeight(); // height in pixels
Texture2D is owned by your game class via std::unique_ptr<Texture2D>. The GPU memory is freed when the unique_ptr is destroyed.
Loading Textures
CNA supports two loading paths:
Option A: Direct file load (simplest)
Pass a file path directly to the Texture2D constructor:
void MyGame::LoadContent() {
spriteBatch_ = std::make_unique<SpriteBatch>(getGraphicsDeviceProperty());
// Load a PNG from the assets/ directory
// Path is relative to the executable
player_ = std::make_unique<Texture2D>("assets/player.png",
getGraphicsDeviceProperty());
background_ = std::make_unique<Texture2D>("assets/background.png",
getGraphicsDeviceProperty());
}
Option B: ContentManager (XNA-style)
The Content member (of type ContentManager) provides the XNA-style asset pipeline. It caches assets and resolves paths relative to a root directory:
void MyGame::Initialize() {
Game::Initialize();
// Set the root directory for content
Content.setRootDirectory("assets");
}
void MyGame::LoadContent() {
spriteBatch_ = std::make_unique<SpriteBatch>(getGraphicsDeviceProperty());
// Loads assets/player.png (extension added automatically if texture.json found)
playerTex_ = Content.Load<Texture2D>("player");
// Loads assets/ui/button.png
buttonTex_ = Content.Load<Texture2D>("ui/button");
}
The ContentManager path strips the extension and expects either a raw image file or a texture.json descriptor alongside the image.
The texture.json Descriptor
CNA's content pipeline supports JSON descriptors alongside image files. A player.json next to player.png can specify filtering and wrapping:
{
"file": "player.png",
"filter": "Linear",
"wrap": "Clamp"
}
Available filter values: "Point" (nearest-neighbour, pixel art look), "Linear" (smooth, default), "Anisotropic".
Available wrap values: "Wrap" (tile), "Clamp" (clamp to edge), "Mirror".
If no descriptor is present, CNA loads the PNG directly with linear filtering and clamp wrap mode.
Draw Overloads
SpriteBatch::Draw() has several overloads with increasing control. Here they are from simplest to most complete:
1. Position only
// Draw at (x, y), no tint (White), full texture
spriteBatch_->Draw(*tex_, Vector2(100.0f, 200.0f), Color::White);
2. Position with tint
// Draw tinted red at position
spriteBatch_->Draw(*tex_, Vector2(100.0f, 200.0f), Color::Red);
3. Destination rectangle
// Stretch texture to fill a 200x150 rectangle at (50,50)
spriteBatch_->Draw(*tex_, Rectangle(50, 50, 200, 150), Color::White);
4. Source rectangle (draw sub-region)
// Draw only the top-left 32x32 pixels of the texture
Rectangle src(0, 0, 32, 32);
spriteBatch_->Draw(*tex_, Vector2(100.0f, 100.0f), src, Color::White);
5. Full control overload
// Full signature:
// Draw(texture, position, sourceRect, color, rotation, origin, scale, effects, depth)
Vector2 position(400.0f, 300.0f); // screen position
Rectangle source(0, 0, 64, 64); // source region (nullptr for whole texture)
Color color = Color::White;
float rotation = 0.5f; // radians
Vector2 origin(32.0f, 32.0f); // rotation pivot (centre of 64x64 sprite)
float scale = 2.0f; // 2x size
SpriteEffects fx = SpriteEffects::None;
float depth = 0.0f; // z-depth for sorting (0=front, 1=back)
spriteBatch_->Draw(*tex_, position, source, color,
rotation, origin, scale, fx, depth);
Parameters explained:
| Parameter | Type | Description |
|---|---|---|
position | Vector2 | Top-left (or origin point) on screen |
sourceRectangle | Rectangle | Region of the texture to draw; omit for whole texture |
color | Color | Tint color; White = no tint |
rotation | float | Rotation in radians, clockwise |
origin | Vector2 | Pivot point in texture space (pixels); (0,0) = top-left |
scale | float or Vector2 | Scale multiplier (1.0 = original size) |
effects | SpriteEffects | Flip flags |
layerDepth | float | 0..1, used with BackToFront / FrontToBack sorting |
Practical examples
// Draw a sprite centred at screen centre, rotated, 2x scale
auto& vp = getGraphicsDeviceProperty().getViewport();
Vector2 screenCentre(vp.getWidth() / 2.0f, vp.getHeight() / 2.0f);
Vector2 texCentre(tex_->getWidth() / 2.0f, tex_->getHeight() / 2.0f);
spriteBatch_->Draw(*tex_, screenCentre, std::nullopt, Color::White,
rotation_, texCentre, 2.0f, SpriteEffects::None, 0.0f);
// Update rotation in Update():
rotation_ += MathHelper::Pi * dt; // rotate 180 degrees per second
// Non-uniform scale: stretch width only
Vector2 scaleXY(3.0f, 1.0f); // triple width, normal height
spriteBatch_->Draw(*tex_, Vector2(50.0f, 50.0f), std::nullopt,
Color::White, 0.0f, Vector2::Zero, scaleXY,
SpriteEffects::None, 0.0f);
SpriteEffects: Flipping
SpriteEffects is an enum used to flip sprites horizontally or vertically — essential for characters that face both left and right:
#include "Microsoft/Xna/Framework/Graphics/SpriteEffects.hpp"
// No flip (default)
SpriteEffects::None
// Mirror horizontally (flip left-right) — for a character facing left
SpriteEffects::FlipHorizontally
// Mirror vertically (flip up-down) — less common
SpriteEffects::FlipVertically
// Flip both axes
SpriteEffects::FlipBoth // = FlipHorizontally | FlipVertically
Typical usage — a character that faces the direction of movement:
// In Update():
if (velocity_.X < 0.0f) {
facingLeft_ = true;
} else if (velocity_.X > 0.0f) {
facingLeft_ = false;
}
// In Draw():
SpriteEffects flip = facingLeft_
? SpriteEffects::FlipHorizontally
: SpriteEffects::None;
spriteBatch_->Draw(*playerTex_, position_, std::nullopt, Color::White,
0.0f, Vector2::Zero, 1.0f, flip, 0.0f);
You now know how to load image files and draw them with full control. In Tutorial 09 you will add text to your game using SpriteFont.