DirectXTKでゲーム制作 第1回:DirectXTKとは何か
DirectXTKの役割、できること、ゲームエンジンとの違い、主要APIの分類を整理します。
Direct3D 11でゲームを作ろうとすると、画面に1枚の画像を出すだけでも、頂点やシェーダー、描画状態、テクスチャなど多くの準備が必要になります。DirectXTK(DirectX Tool Kit)は、そうした定番処理を使いやすいC++クラスとしてまとめたライブラリです。
ただし、DirectXTKはゲームエンジンではありません。ここを最初に区別しておくと、「何を任せられて、何を自分で作るのか」が見通しやすくなります。
DirectXTKとは
DirectXTKは、MicrosoftがGitHubで公開している、Direct3D向けのヘルパークラス集です。Direct3Dそのものを置き換えるのではなく、その上に薄い便利な層を加えます。
たとえば、2D画像をまとめて描画するSpriteBatch、ビットマップフォントを描画するSpriteFont、基本的な3D形状を生成して描くGeometricPrimitiveなどが用意されています。毎回ゼロから書くと長くなりやすい処理を、再利用しやすい形で使えるのが特徴です。

重要なのは、DirectXTKを使ってもDirect3D 11を扱っている事実は変わらないことです。デバイスやデバイスコンテキスト、レンダーターゲット、フレーム単位の更新と描画といった基本構造は、アプリケーション側が管理します。
DirectXTKで何ができるか
DirectXTKの機能は、目的別の部品として捉えると分かりやすくなります。

2D描画と文字
SpriteBatchは、テクスチャを2Dスプライトとして効率よく描画するためのクラスです。位置、回転、拡大縮小、色、描画順などを指定できます。SpriteFontと組み合わせれば、画面上に文字を描画できます。HUD、メニュー、デバッグ表示などに向いています。
3D描画の補助
GeometricPrimitiveは立方体や球などの基本形状を生成して描画します。Modelは対応形式のメッシュを読み込み、Effectsは一般的な描画に使える組み込みシェーダーを提供します。PrimitiveBatchやVertexTypesは、独自の頂点を使った描画を組み立てるときに役立ちます。
テクスチャの読み込みと画面保存
DDSTextureLoaderはDDS、WICTextureLoaderはPNGやJPEGなどをDirect3Dのテクスチャとして読み込むための軽量ローダーです。ScreenGrabを使えば、描画結果を画像として保存できます。
描画状態、リソース、数学
CommonStatesは、ブレンド、深度ステンシル、ラスタライザー、サンプラーといった定番の描画状態をまとめて提供します。BufferHelpersやGraphicsMemoryはリソース作成や動的メモリ管理を補助し、SimpleMathはDirectXMathを扱いやすいC++型で包みます。
入力
Keyboard、Mouse、GamePadは、各入力デバイスの現在状態や前フレームからの変化を扱いやすくします。入力APIの細部を毎回直接処理する負担を減らせます。
音声
AudioEngine、SoundEffect、WaveBankなどを使い、XAudio2を基盤とした効果音や音声の再生を組み立てられます。
DirectXTKはゲームエンジンではない
UnityやUnreal Engineのようなゲームエンジンは、描画だけでなく、シーン管理、オブジェクト管理、物理、アニメーション、エディタ、アセットの取り込みなど、制作全体を支える大きな枠組みを提供します。
DirectXTKが提供するのは、そのうちの一部で使える便利な部品です。ゲームの実行方式やデータ構造まで決めるものではありません。

つまり、DirectXTKを採用しても、少なくとも次のような部分は自分で設計します。
- 更新と描画を回すゲームループ
- タイトル、ゲーム中、結果画面などの状態遷移
- キャラクターや敵、弾などのオブジェクト管理
- 当たり判定、ゲームルール、AI
- アセットの所有方法と読み込みのタイミング
DirectXTKは「ゲームを完成させる仕組み」ではなく、「Direct3D 11でゲームを作るときの定番作業を短くする道具」と考えるのが適切です。
初期化処理を任せると、利用側のコードはどう変わるか
DirectXTKの公式チュートリアルやVisual Studioテンプレートでは、DeviceResourcesという補助クラスを使って、Direct3Dデバイス、デバイスコンテキスト、スワップチェーン、レンダーターゲット、深度バッファなどの初期化と管理をまとめます。
ここでは、800×600のウィンドウを作り、背景色でクリアして表示するだけの最小例を比較します。どちらもWin32のウィンドウ作成とメッセージループは必要ですが、Direct3D 11の初期化部分が大きく変わります。
Direct3D 11を直接初期化する場合
直書きでは、スワップチェーンの設定、デバイスとコンテキストの作成、バックバッファの取得、レンダーターゲットビューの作成、ビューポートの設定、COMオブジェクトの保持を自分で行います。
#include <windows.h>
#include <d3d11.h>
#include <wrl/client.h>
#pragma comment(lib, "d3d11.lib")
using Microsoft::WRL::ComPtr;
ComPtr<ID3D11Device> g_device;
ComPtr<ID3D11DeviceContext> g_context;
ComPtr<IDXGISwapChain> g_swapChain;
ComPtr<ID3D11RenderTargetView> g_renderTarget;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
if (msg == WM_DESTROY)
{
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wp, lp);
}
bool InitializeDirect3D(HWND hwnd)
{
DXGI_SWAP_CHAIN_DESC desc{};
desc.BufferDesc.Width = 800;
desc.BufferDesc.Height = 600;
desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.BufferCount = 2;
desc.OutputWindow = hwnd;
desc.Windowed = TRUE;
desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
D3D_FEATURE_LEVEL level{};
if (FAILED(D3D11CreateDeviceAndSwapChain(
nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0,
nullptr, 0, D3D11_SDK_VERSION,
&desc, &g_swapChain, &g_device, &level, &g_context)))
{
return false;
}
ComPtr<ID3D11Texture2D> backBuffer;
if (FAILED(g_swapChain->GetBuffer(
0, IID_PPV_ARGS(backBuffer.GetAddressOf()))))
{
return false;
}
if (FAILED(g_device->CreateRenderTargetView(
backBuffer.Get(), nullptr, &g_renderTarget)))
{
return false;
}
D3D11_VIEWPORT viewport{};
viewport.Width = 800.0f;
viewport.Height = 600.0f;
viewport.MaxDepth = 1.0f;
g_context->RSSetViewports(1, &viewport);
return true;
}
int WINAPI wWinMain(HINSTANCE instance, HINSTANCE, PWSTR, int show)
{
WNDCLASS wc{};
wc.lpfnWndProc = WndProc;
wc.hInstance = instance;
wc.lpszClassName = L"RawD3D11Window";
RegisterClass(&wc);
RECT rect{ 0, 0, 800, 600 };
AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
HWND hwnd = CreateWindowEx(
0, wc.lpszClassName, L"Direct3D 11 直書き",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
rect.right - rect.left, rect.bottom - rect.top,
nullptr, nullptr, instance, nullptr);
if (!hwnd || !InitializeDirect3D(hwnd)) return 1;
ShowWindow(hwnd, show);
MSG msg{};
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
continue;
}
const float color[] = { 0.08f, 0.12f, 0.20f, 1.0f };
ID3D11RenderTargetView* target = g_renderTarget.Get();
g_context->OMSetRenderTargets(1, &target, nullptr);
g_context->ClearRenderTargetView(target, color);
g_swapChain->Present(1, 0);
}
return 0;
}DirectXTK公式テンプレートのDeviceResourcesを使う場合
DeviceResourcesをプロジェクトに用意しておけば、利用側ではウィンドウを渡し、デバイス依存リソースとウィンドウサイズ依存リソースの作成を呼び出すだけです。描画時も、必要なオブジェクトをアクセサーから取得し、最後にPresentを呼びます。
#include <windows.h>
#include <memory>
#include "DeviceResources.h"
std::unique_ptr<DX::DeviceResources> g_deviceResources;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
if (msg == WM_DESTROY)
{
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wp, lp);
}
int WINAPI wWinMain(HINSTANCE instance, HINSTANCE, PWSTR, int show)
{
WNDCLASS wc{};
wc.lpfnWndProc = WndProc;
wc.hInstance = instance;
wc.lpszClassName = L"DirectXTKWindow";
RegisterClass(&wc);
RECT rect{ 0, 0, 800, 600 };
AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
HWND hwnd = CreateWindowEx(
0, wc.lpszClassName, L"DirectXTK + DeviceResources",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
rect.right - rect.left, rect.bottom - rect.top,
nullptr, nullptr, instance, nullptr);
if (!hwnd) return 1;
ShowWindow(hwnd, show);
// Direct3D 11の初期化をDeviceResourcesへ任せる
g_deviceResources = std::make_unique<DX::DeviceResources>(
DXGI_FORMAT_B8G8R8A8_UNORM,
DXGI_FORMAT_UNKNOWN); // 今回は深度バッファ不要
g_deviceResources->SetWindow(hwnd, 800, 600);
g_deviceResources->CreateDeviceResources();
g_deviceResources->CreateWindowSizeDependentResources();
MSG msg{};
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
continue;
}
auto context = g_deviceResources->GetD3DDeviceContext();
auto target = g_deviceResources->GetRenderTargetView();
auto viewport = g_deviceResources->GetScreenViewport();
const float color[] = { 0.08f, 0.12f, 0.20f, 1.0f };
context->OMSetRenderTargets(1, &target, nullptr);
context->RSSetViewports(1, &viewport);
context->ClearRenderTargetView(target, color);
g_deviceResources->Present();
}
return 0;
}比較すると、直書き版にあったDXGI_SWAP_CHAIN_DESCの設定、D3D11CreateDeviceAndSwapChain、バックバッファの取得、レンダーターゲットビューの生成が、利用側のコードから消えています。さらに公式のDeviceResourcesは、ウィンドウサイズ変更時のリソース再作成、デバイスロストからの復旧、デバッグレイヤーがない環境でのフォールバックなどもまとめて扱います。
DirectXTKを使う利点は、Direct3D 11を使わなくてよくなることではありません。毎回必要になる初期化と管理を信頼できる共通部品へ任せ、ゲーム側のコードを描画やゲームロジックに集中させられることです。
注:DeviceResourcesはDirectXTK本体のNuGetライブラリに含まれる単一APIではなく、DirectXTK公式のVisual Studioテンプレートやサンプルで提供される補助クラスです。この比較は、公式テンプレートを使ったときに自分が記述・保守するMain.cppの差を示しています。
使うメリット
最大のメリットは、定番処理の実装量を減らしつつ、Direct3D 11の構造を隠しすぎないことです。高水準のゲームエンジンほど自動化はしませんが、その分、レンダリングの流れやリソースの扱いを自分で決められます。(特に、DirectXでのプログラミングを求められるゲーム業界志望の人におすすめです)
そのためDirectXTKは、Direct3D 11を学びながら小規模なゲームや描画アプリケーションを作りたい場合や、独自フレームワークの土台に必要な機能だけ取り込みたい場合に向いています。
DirectXTexとの関係
名前の似たDirectXTexなんてのもあります。同じDirectXTKのライブラリのセットにも入ってたりしますが、主に画像の読み書き、DDSの読み書き、リサイズ、形式変換、ミップマップ生成、ブロック圧縮などを行う、本格的なテクスチャ処理ライブラリです。主に制作ツールやコンテンツパイプラインでの利用を想定しています。まぁ、使う時が来たらまた説明します。
一方、DirectXTKのDDSTextureLoaderとWICTextureLoaderは、ゲーム実行時に加工済みの画像を軽量に読み込むためのものです。ゲーム内で画像を表示するだけなら、まずDirectXTKのローダーで十分です。画像を事前変換・圧縮する工程が必要になったときに、DirectXTexを別の道具として検討します。
まとめ
- DirectXTKは、Direct3D向けのC++ヘルパークラス集
- 2D・3D描画、テクスチャ、入力、音声、数学などの定番処理を補助する
- Direct3D 11を置き換えるものではなく、ゲームエンジンでもない
- ゲームループ、シーン、ルール、オブジェクト管理は自分で設計する
- DirectXTexは主にテクスチャの加工や制作パイプラインを担う別ライブラリ