Tutorial 08: Loading and Drawing Textures
What you’ll learn
- Loading a
Texture2DthroughContentManager. - Why CNA needs no separate texture descriptor file.
- The
SpriteBatch::Drawoverloads, and flipping withSpriteEffects.
Before you start — Tutorial 06: Drawing Your First 2D Shape for the SpriteBatch workflow, and Tutorial 07: Working with Colors for the tint parameter.
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->getWidthProperty(); // width in pixels
int h = texture->getHeightProperty(); // 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.setRootDirectoryProperty("assets");
}
void MyGame::LoadContent() {
spriteBatch_ = std::make_unique<SpriteBatch>(getGraphicsDeviceProperty());
// Loads assets/player.png — the extension is resolved automatically
playerTex_ = getContentProperty().Load<Texture2D>("player");
// Loads assets/ui/button.png
buttonTex_ = getContentProperty().Load<Texture2D>("ui/button");
}
The ContentManager path strips the extension and resolves the asset name against the image formats CNA can decode.
There Is No Texture Descriptor
Textures have no sidecar or descriptor file. Load<Texture2D> resolves your asset name against .png, .jpg, .jpeg, .bmp, .gif, .tga, .tif, .tiff and .qoi, and decodes the first one it finds. There is nothing else to configure at load time.
Filtering and wrapping are not properties of the asset. As in XNA, they are sampler state on the graphics device, applied when you draw — which means the same texture can be sampled differently in different batches:
#include "Microsoft/Xna/Framework/Graphics/SamplerState.hpp"
auto& gd = getGraphicsDeviceProperty();
// Point sampling for a crisp pixel-art look:
gd.getSamplerStatesProperty()[0] = SamplerState::PointClamp;
// Or tile a texture across a larger quad:
gd.getSamplerStatesProperty()[0] = SamplerState::LinearWrap;
SpriteBatch::Begin() also takes a SamplerState* directly, which is the usual way to set it for 2D drawing.
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().getViewportProperty();
Vector2 screenCentre(vp.getWidthProperty() / 2.0f, vp.getHeightProperty() / 2.0f);
Vector2 texCentre(tex_->getWidthProperty() / 2.0f, tex_->getHeightProperty() / 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.