ゲーム用にウィンドウを設定する
DirectXを初期化する時、解像度を1280x720にしてるので、最終的にウィンドウに表示される画像は1280x720です。しかし、現在のウィンドウのサイズはぴったり1280x720にはなっていないし、さらにサイズを自由に変更できてしまいます。このままでは変に横長にも縦長にも出来てしまいます。
ウィンドウのサイズをきっちり設定して、サイズも固定しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
~省略~ // // 関数: InitInstance(HINSTANCE, int) // // 目的: インスタンス ハンドルを保存して、メイン ウィンドウを作成します // // コメント: // // この関数で、グローバル変数でインスタンス ハンドルを保存し、 // メイン プログラム ウィンドウを作成および表示します。 // BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { hInst = hInstance; // グローバル変数にインスタンス ハンドルを格納する // ウィンドウのサイズを変更できないように、WS_THICKFRAMEは除外する HWND hWnd = CreateWindowW( szWindowClass, szTitle, WS_OVERLAPPEDWINDOW - WS_THICKFRAME, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); if (!hWnd) { return FALSE; } // Direct3Dインスタンス作成 Direct3D::CreateInstance(); // Direct3D初期化 D3D.Initialize(hWnd, 1280, 720); // ウィンドウのクライアントサイズを設定 RECT rcWnd, rcClient; GetWindowRect(hWnd, &rcWnd); GetClientRect(hWnd, &rcClient); int newWidth = (rcWnd.right - rcWnd.left) - (rcClient.right - rcClient.left) + 1280; int newHeight = (rcWnd.bottom - rcWnd.top) - (rcClient.bottom - rcClient.top) + 720; SetWindowPos(hWnd, NULL, 0, 0, newWidth, newHeight, SWP_NOMOVE | SWP_NOZORDER); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } ~省略~ |
WS_OVERLAPPEDWINDOW - WS_THICKFRAME
WS_OVERLAPPEDWINDOWからWS_THICKFRAMEのフラグを除外することで、ウィンドウのサイズをマウスで変更できなくなります。
// ウィンドウのクライアントサイズを設定
RECT rcWnd, rcClient;
GetWindowRect(hWnd, &rcWnd);
GetClientRect(hWnd, &rcClient);
int newWidth = (rcWnd.right - rcWnd.left) - (rcClient.right - rcClient.left) + 1280;
int newHeight = (rcWnd.bottom - rcWnd.top) - (rcClient.bottom - rcClient.top) + 720;
SetWindowPos(hWnd, NULL, 0, 0, newWidth, newHeight, SWP_NOMOVE | SWP_NOZORDER);
ここでウィンドウのクライアント(画像が表示される範囲)のサイズを設定しています。SetWindowPos関数でウィンドウ全体のサイズは設定できるのですが、クライアントのサイズのみを設定できる関数はありません。なので、計算でウィンドウのサイズ=余白 + クライアントサイズを求め設定しています。
メニューを消す
ウィンドウの上にある、ファイル(F)やヘルプ(H)は不要なので消しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
~省略~ // // 関数: MyRegisterClass() // // 目的: ウィンドウ クラスを登録します。 // ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEXW wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION)); wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = NULL; wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); return RegisterClassExW(&wcex); } ~省略~ |
wcex.lpszMenuNameにNULLを入れるだけで、メニューは表示されなくなります。
2D描画機能をDirect3Dクラスへまとめる
いまGameSystemクラスのExecute関数で、その場で頂点データを作って、頂点バッファも作って、描画に必要なものをいろいろセットして、そして描画と、非常に煩雑な書き方になっています。画像1枚表示するだけで一苦労なので、もっと簡単に記述が出来た方がいいですね。今回はDirect3Dクラスのメンバとして、簡単に2D描画ができる関数を作って便利にしましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
#pragma once #include "DirectX.h" class Texture; // 2D用頂点構造体 struct VertexType2D { DirectX::XMFLOAT3 Pos; // 座標 DirectX::XMFLOAT2 UV; // UV座標 }; //========================================= // Direct3Dクラス //========================================= class Direct3D { public: // ※変数は今回は全てpublicにしますが、本来はprivateで隠すべき※ // Direct3Dデバイス ComPtr<ID3D11Device> m_device; // Direct3Dデバイスコンテキスト ComPtr<ID3D11DeviceContext> m_deviceContext; // スワップチェイン ComPtr<IDXGISwapChain> m_swapChain; // バックバッファーのRTビュー ComPtr<ID3D11RenderTargetView> m_backBufferView; //-------------------------------------------- // Direct3Dを初期化し、使用できるようにする関数 // hWnd : ウィンドウハンドル // width : 画面の幅 // height : 画面の高さ //-------------------------------------------- bool Initialize(HWND hWnd, int width, int height); // 2D描画用のシェーダー ComPtr<ID3D11VertexShader> m_spriteVS = nullptr; // 頂点シェーダー ComPtr<ID3D11PixelShader> m_spritePS = nullptr; // ピクセルシェーダー ComPtr<ID3D11InputLayout> m_spriteInputLayout = nullptr;// 入力レイアウト ComPtr<ID3D11Buffer> m_vbSquare; // 四角形用頂点バッファ // 2D描画モードにする void ChangeMode_2D(); // 2D描画 // tex : テクスチャ // x : x座標 // y : y座標 // w : 幅 // h : 高さ void Draw2D(const Texture& tex, float x, float y, float w, float h); //========================================= // 今回このクラスは、どこからでもアクセスできるように // シングルトンパターンにします // ↓↓↓以下、シングルトンパターンのコード //========================================= private: ~省略~ |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
~省略~ void Direct3D::ChangeMode_2D() { // 2D用頂点シェーダーセット m_deviceContext->VSSetShader(m_spriteVS.Get(), 0, 0); // 2D用ピクセルシェーダーセット m_deviceContext->PSSetShader(m_spritePS.Get(), 0, 0); // 入力レイアウトセット m_deviceContext->IASetInputLayout(m_spriteInputLayout.Get()); // 四角形用 頂点バッファ作成(初回のみ) if (m_vbSquare == nullptr) { D3D11_BUFFER_DESC vbDesc = {}; vbDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; // デバイスにバインドするときの種類(頂点バッファ、インデックスバッファ、定数バッファなど) vbDesc.ByteWidth = sizeof(VertexType2D) * 4; // 作成するバッファのバイトサイズ vbDesc.MiscFlags = 0; // その他のフラグ vbDesc.StructureByteStride = 0; // 構造化バッファの場合、その構造体のサイズ vbDesc.Usage = D3D11_USAGE_DYNAMIC; // 作成するバッファの使用法 vbDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; m_device->CreateBuffer(&vbDesc, nullptr, &m_vbSquare); } // 頂点バッファを描画で使えるようにセットする UINT stride = sizeof(VertexType2D); UINT offset = 0; m_deviceContext->IASetVertexBuffers(0, 1, m_vbSquare.GetAddressOf(), &stride, &offset); // プロミティブ・トポロジーをセット m_deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); // サンプラーステートを作成しセットする { // 異方性フィルタリング補間、Wrapモード D3D11_SAMPLER_DESC desc = {}; desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; // 線形フィルタリング desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; // テクスチャアドレッシングモードをWrapに desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; // テクスチャアドレッシングモードをWrapに desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; // テクスチャアドレッシングモードをWrapに desc.MipLODBias = 0; desc.MaxAnisotropy = 0; desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; desc.BorderColor[0] = desc.BorderColor[1] = desc.BorderColor[2] = desc.BorderColor[3] = 0; desc.MinLOD = 0; desc.MaxLOD = D3D11_FLOAT32_MAX; // ステートオブジェクト作成 ComPtr<ID3D11SamplerState> state; m_device->CreateSamplerState(&desc, &state); // 各シェーダーの0番目にセット(実際は必要なシェーダーだけセットしてください) m_deviceContext->VSSetSamplers(0, 1, state.GetAddressOf()); // 頂点シェーダーの0番目にセット m_deviceContext->PSSetSamplers(0, 1, state.GetAddressOf()); // ピクセルシェーダーの0番目にセット m_deviceContext->GSSetSamplers(0, 1, state.GetAddressOf()); // ジオメトリシェーダーの0番目にセット m_deviceContext->CSSetSamplers(0, 1, state.GetAddressOf()); // コンピュートシェーダーの0番目にセット } } void Direct3D::Draw2D(const Texture& tex, float x, float y, float w, float h) { float hW = w * 0.5f; float hH = h * 0.5f; // 頂点データ作成 VertexType2D v[4] = { {{x - hW, y - hH, 0}, {0, 1}}, // 左下 {{x - hW, y + hH, 0}, {0, 0}}, // 左上 {{x + hW, y - hH, 0}, {1, 1}}, // 右下 {{x + hW, y + hH, 0}, {1, 0}}, // 右上 }; // 頂点バッファにデータを書き込む D3D11_MAPPED_SUBRESOURCE pData; if (SUCCEEDED(m_deviceContext->Map(m_vbSquare.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &pData))) { // データコピー memcpy_s(pData.pData, sizeof(v), &v[0], sizeof(v)); m_deviceContext->Unmap(m_vbSquare.Get(), 0); } // テクスチャを、スロット0にセット m_deviceContext->PSSetShaderResources(0, 1, tex.m_srv.GetAddressOf()); // デバイスコンテキストくん、上記のセットした内容で描画してください m_deviceContext->Draw(4, 0); } |
大半がGameSystemクラスのExecuteに書いていた内容を、若干名前を変更したりして持ってきてるだけです。
ChangeMode_2D関数
この関数を実行することで、2D描画に必要なものを作成したり、それらをデバイスコンテキストにセットしたりします。2D描画を行う前には最低でも1度この関数を実行することになります。
四角形用 頂点バッファ作成(初回のみ)
頂点バッファを毎フレーム作成するのはパフォーマンスに多大な悪影響がでるので、最初に1度だけ作成して、それを使いまわせるようにしましょう。
vbDesc.Usage = D3D11_USAGE_DYNAMIC; と vbDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; を指定することで動的バッファとなり、使いまわせるバッファになります。
また、初回のChangeMode_2D関数を実行したときだけ作成されるように、ifで判定もしています。
サンプラーステートを作成しセットする
以前説明したように、サンプラーステートとはテクスチャから色を取得できる道具のようなものです。いままでは何もセットしていなくても動いていましたが、本来はしっかりセットしておく必要があります(実際に出力ウィンドウに警告が出まくっています)。
Draw2D関数
指定した座標(x, y)とサイズ(幅、高)で、テクスチャを2D描画する関数です。あらかじめ作成しておいた頂点バッファに頂点データを書き込み、描画コマンドを発行(描画を実行)しています。
使用する
では実際に使用しましょう。GameSystemクラスのExecute関数の内容で、四角形を描画していることろを全部けし、以下のようにします。Draw2D関数を実行するだけで、簡単に何枚でも描画できるようになりました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
#include "framework.h" // DirectXクラスを使えるようにする #include "Source/DirectX/DirectX.h" // GameSystemクラスを使えるようにする #include "GameSystem.h" // ゲームの初期設定を行う void GameSystem::Initialize() { // 画像の読み込み m_tex.Load("Data/Logo.png"); } // このゲームの時間を進める(処理を実行する) void GameSystem::Execute() { // 画面を青色で塗りつぶす float color[4] = { 0.2f, 0.2f, 1.0f, 1.0f }; D3D.m_deviceContext->ClearRenderTargetView(D3D.m_backBufferView.Get(), color); // 三角形の描画 { // 2Dモードに切り替える D3D.ChangeMode_2D(); // 2D描画 D3D.Draw2D(m_tex, 0, 0, 1, 1); } // バックバッファの内容を画面に表示 D3D.m_swapChain->Present(1, 0); } |
とてもシンプルに書けるようになりましたね!