Getting Started with CNA

CNA - C++ XNA 4.0 reimplementation  ·  near-complete XNA 4.0 type coverage

This guide targets CNA 0.1.0-alpha.1, the first tagged pre-release. Start with a single renderer and the default SDL3 platform/audio implementations; multi-renderer selection, SDL2/Headless/Terminal platforms, non-default audio-device selection and compiled Effect Framework bytecode are opt-in topics covered by their dedicated guides. Only CNA_AUDIO_PLATFORM=SDL3 enables the tag's real XNA mixer/playback path. The tag also contains an experimental C API source layer, but its final library target is compile-blocked and is not a usable getting-started path. APIs may change before 1.0, so pin v0.1.0-alpha.1 when following these commands.

What you are getting

CNA is a C++ framework that mirrors the XNA 4.0 programming model. You write game code against Microsoft::Xna::Framework. The default build uses SDL3 for host and audio services, but CNA_PLATFORM, CNA_AUDIO_PLATFORM and graphics-renderer selection are independent.

If you have XNA or MonoGame experience, the patterns will feel familiar. The main difference is C++ instead of C#.

Prerequisites

Linux

  • CMake 3.20 or newer
  • C++23-capable compiler: GCC 12+ or Clang 15+
  • ../sharp-runtime directory (sibling to the CNA repo) - no external dependencies
  • ../easy-gl directory - only needed for the OPENGLES3 renderer
  • SDL3, SDL3_image, and SDL3_mixer are built from vendored submodules - no system packages required

Windows

  • CMake 3.20 or newer
  • One of: MSVC 2022 (v17.8+), clang-cl, or MinGW-w64
  • ..\sharp-runtime directory (sibling to the CNA repo)
  • SDL3 built from vendored submodules - no pre-built binaries or CMAKE_PREFIX_PATH needed

System requirements

ComponentRequirement
CompilerGCC 12+, Clang 15+, or MSVC 2022 v17.8+ (C++23 required)
Build systemCMake 3.20+
GitAny recent version (for submodules)
OpenGL (OPENGLES3 renderer)OpenGL ES 3.0 or OpenGL 3.0+ desktop; GPU driver installed
Vulkan (VULKAN renderer)Vulkan-capable GPU; vulkan-headers / libvulkan-dev
Disk space~500 MB (source + build artifacts)
RAM4 GB minimum for compilation
Sibling repossharp-runtime and (for OPENGLES3) easy-gl cloned alongside cna/

Clone & initialise

git clone https://github.com/openeggbert/cna.git
cd cna
git submodule update --init --recursive

This populates third_party/SDL, third_party/SDL_image, and third_party/SDL_mixer. After this step no system SDL packages are required.

Quick start build (Linux - EasyGL renderer)

git submodule update --init --recursive
cmake -S . -B build -DCNA_GRAPHICS_RENDERER=OPENGLES3
cmake --build build --target CnaTests
ctest --test-dir build --output-on-failure

Quick start build (Linux - SDL_Renderer renderer)

cmake -S . -B build-sdlrenderer -DCNA_GRAPHICS_RENDERER=SDL_RENDERER
cmake --build build-sdlrenderer --target CnaTests

Verify the build

# Run the test suite
ctest --test-dir build --output-on-failure

# Run the hello-triangle verification demo
cmake --build build --target hello-triangle-sdl

Minimal game skeleton

Here is the smallest possible CNA game - a window that clears to cornflower blue, the traditional XNA default clear colour.

#include <memory>

#include "Microsoft/Xna/Framework/Game.hpp"
#include "Microsoft/Xna/Framework/Color.hpp"
#include "Microsoft/Xna/Framework/Graphics/GraphicsDeviceManager.hpp"
#include "Microsoft/Xna/Framework/Graphics/SpriteBatch.hpp"
#include "Microsoft/Xna/Framework/Graphics/Texture2D.hpp"

using namespace Microsoft::Xna::Framework;
using namespace Microsoft::Xna::Framework::Graphics;

class MyGame final : public Game {
public:
    MyGame() : graphics_(this) {}

protected:
    void LoadContent() override {
        spriteBatch_ = std::make_unique<SpriteBatch>(getGraphicsDeviceProperty());
        logo_ = std::make_unique<Texture2D>("assets/logo.png", getGraphicsDeviceProperty());
    }

    void Update(GameTime& gameTime) override {
        (void)gameTime;
        // Update game state here.
    }

    void Draw(const GameTime& gameTime) override {
        (void)gameTime;
        auto& device = getGraphicsDeviceProperty();
        device.Clear(CornflowerBlue);
        spriteBatch_->Begin();
        spriteBatch_->Draw(*logo_, 100.0f, 80.0f);
        spriteBatch_->End();
        device.Present();
    }

private:
    GraphicsDeviceManager graphics_;
    std::unique_ptr<SpriteBatch> spriteBatch_;
    std::unique_ptr<Texture2D> logo_;
};

int main() {
    MyGame game;
    game.Run();
    return 0;
}

Testing your build

CNA's tag contains 568 C++ test source files and 8,263 static GoogleTest-family definitions covering the framework and configuration-specific modules.

# Run all tests
ctest --test-dir build --output-on-failure

# Or build and run directly
cmake --build build --target CnaTests
./build/CnaTests

This runs what the configured build registered. Parameterized tests, platform/renderer selection, optional layers and dependencies change the executable and CTest inventory; run ctest --test-dir build -N to inspect it. See Verification & Known Issues.

3D rendering example

The following example draws a coloured triangle using the EasyGL or Vulkan renderer. It demonstrates VertexBuffer, BasicEffect, and the DrawPrimitives call.

#include "Microsoft/Xna/Framework/Game.hpp"
#include "Microsoft/Xna/Framework/Graphics/GraphicsDeviceManager.hpp"
#include "Microsoft/Xna/Framework/Graphics/BasicEffect.hpp"
#include "Microsoft/Xna/Framework/Graphics/VertexBuffer.hpp"
#include "Microsoft/Xna/Framework/Graphics/VertexPositionColor.hpp"

using namespace Microsoft::Xna::Framework;
using namespace Microsoft::Xna::Framework::Graphics;

class TriangleGame final : public Game {
public:
    TriangleGame() : graphics_(this) {
        graphics_.setPreferredBackBufferWidthProperty(800);
        graphics_.setPreferredBackBufferHeightProperty(600);
    }

protected:
    void LoadContent() override {
        effect_ = std::make_unique<BasicEffect>(getGraphicsDeviceProperty());
        effect_->VertexColorEnabled = true;

        VertexPositionColor vertices[] = {
            { Vector3( 0.0f,  0.5f, 0.0f), Color::Red   },
            { Vector3( 0.5f, -0.5f, 0.0f), Color::Green },
            { Vector3(-0.5f, -0.5f, 0.0f), Color::Blue  },
        };
        vb_ = std::make_unique<VertexBuffer>(
            getGraphicsDeviceProperty(),
            VertexPositionColor::VertexDeclaration,
            3, BufferUsage::None);
        vb_->SetData(vertices, 3);
    }

    void Update(GameTime&) override {}

    void Draw(const GameTime&) override {
        auto& gd = getGraphicsDeviceProperty();
        gd.Clear(Color::CornflowerBlue);

        effect_->setWorldProperty(Matrix::Identity);
        effect_->setViewProperty(Matrix::CreateLookAt(
            Vector3(0, 0, 2), Vector3::Zero, Vector3::Up));
        effect_->setProjectionProperty(Matrix::CreatePerspectiveFieldOfView(
            MathHelper::PiOver4, 800.0f / 600.0f, 0.1f, 100.0f));

        gd.SetVertexBuffer(*vb_);
        for (auto& pass : effect_->getCurrentTechniqueProperty().Passes) {
            pass.Apply();
            gd.DrawPrimitives(PrimitiveType::TriangleList, 0, 1);
        }
        gd.Present();
    }

private:
    GraphicsDeviceManager graphics_;
    std::unique_ptr<BasicEffect> effect_;
    std::unique_ptr<VertexBuffer> vb_;
};

int main() { TriangleGame game; game.Run(); }

Build with the OPENGLES3 or VULKAN renderer — 3D rendering is not available on SDL_RENDERER. See the 3D Rendering guide for the full API.

Next steps

External references

CNA targets the XNA 4.0 API surface. These references document the target API: