2025/12/7(日)
はじまり
さて、はじまりましたこのコーナー。主が休みの日に2時間ほど使って自由にプログラミングしていくコーナーです。説明しながらやっていくので、あまり内容を書けないし、いろいろ端折ったりすると思いますが、そこはご愛敬でお願いいたします。何をやっていくのかというと、自分が思うようにプログラミングしていき、その説明をするだけです。筆者は基本的にC++のDirect2Dか、JavaScriptのCanvasを使ってグラフィックスプログラミングを主にやっていきます。やはり見た目でわかりやすいほうが楽しめると思いませんか?
今日やるのはC++です。プログラミングをするといったらC++が真っ先に思い浮かびます。C++はJavaScriptやPythonよりも圧倒的に高速で動作し、静的型付き言語なので、プログラミングをしている感が強いものです。ただ、Direct2Dで描画しようと思うと、最初のコードが結構長くなってしまいます。慣れれば高速で書いて15分ほどで書けるかと思われます。ただ、コードを書いているだけでもプログラミングした気になれるので、ただプログラミングがしたいといった場合にはDirect2Dで描画しても良いでしょう。てなわけで、Direct2Dで円を描画します。
基本
Visual Studioを開いたら、[新しいプロジェクトの作成]→[Windowsデスクトップウィザード]を選択し、プロジェクト名を入れ、[作成]を押し、アプリケーションの種類を[デスクトップアプリケーション]、追加のオプションで[空のプロジェクト]にチェックを入れ、[作成]を押します。
プロジェクトを作成したら、ソリューションエクスプローラーのソースファイルを右クリックして、新しいファイルを追加しましょう。ここでは「main.cpp」とすることにします。コードを書く部分に、ウィンドウ作成のコードを書いていきましょう。次のコードを入力します。
#include <windows.h>
const int WIDTH = 640;
const int HEIGHT = 480;
const wchar_t TITLE[] = L"タイトル";
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
HWND m_hwnd;
int WINAPI wWinMain(_In_ HINSTANCE hInst, _In_opt_ HINSTANCE hPrev, _In_ PWSTR pCmd, _In_ int nCmd)
{
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInst;
wc.lpszClassName = L"Window Class";
RegisterClass(&wc);
RECT rc = { 0, 0, WIDTH, HEIGHT };
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
m_hwnd = CreateWindowEx(0, wc.lpszClassName, TITLE,
WS_OVERLAPPEDWINDOW ^ WS_SIZEBOX ^ WS_MAXIMIZEBOX,
CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top,
NULL, NULL, hInst, NULL);
if (m_hwnd == NULL) return 0;
ShowWindow(m_hwnd, nCmd);
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
実行するとウィンドウが表示されるはずです。大事なのは、WIDTHにウィンドウの幅、HEIGHTに高さ、TITLEにウィンドウのタイトルを入れていることと、wWinMainが最初に実行される関数で、WindowProcがイベントを処理する関数となっていることです。ウィンドウの作成は定型として覚えるのが無難です。
次に、Direct2Dを使って背景を塗ります。次のコードを書きましょう。
#include <windows.h>
#include <d2d1.h>
#pragma comment(lib, "d2d1")
#include <atlbase.h>
using D2D1::ColorF;
const int WIDTH = 640;
const int HEIGHT = 480;
const wchar_t TITLE[] = L"タイトル";
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
HRESULT CreateResources();
void OnPaint();
HWND m_hwnd;
CComPtr<ID2D1Factory> factory;
CComPtr<ID2D1HwndRenderTarget> rt;
CComPtr<ID2D1SolidColorBrush> brush;
≀省略
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
if (FAILED(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &factory)))
{
return -1;
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
OnPaint();
return 0;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
HRESULT CreateResources()
{
HRESULT hr = S_OK;
if (rt == NULL)
{
D2D1_SIZE_U size = D2D1::SizeU(WIDTH, HEIGHT);
hr = factory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(m_hwnd, size), &rt);
if (SUCCEEDED(hr))
{
hr = rt->CreateSolidColorBrush(ColorF(ColorF::Black), &brush);
}
}
return hr;
}
void OnPaint()
{
HRESULT hr = CreateResources();
if (SUCCEEDED(hr))
{
PAINTSTRUCT ps;
BeginPaint(m_hwnd, &ps);
rt->BeginDraw();
rt->Clear(ColorF(ColorF::Ivory));
rt->EndDraw();
EndPaint(m_hwnd, &ps);
InvalidateRect(m_hwnd, NULL, FALSE);
}
}
実行したら、ウィンドウの背景が薄い黄色で塗られているはずです。ここで大事なのは、OnPaint関数のrt->Clear()で背景を薄い黄色にしている点です。最後のInvalidateRect関数で画面を更新しています。
円を描く
どうやってページを書くか考えていたら長くなってしまったので、今回は円を描いて終わりにしましょう。また、一つのファイルにウィンドウの作成とDirect2Dの基本コード、円の描画まで書くと見にくくなってきたので、次回からはちゃんとファイル分割して見やすくしていきます。とりあえず、円を描く関数を作り、円を描いて終わりましょう。OnPaint関数の上にDrawCircle関数を作り、OnPaintの中で呼び出します。次のコードを書きましょう。
≀
void DrawCircle(float x, float y, float r, ColorF col)
{
D2D1_ELLIPSE ellipse = D2D1::Ellipse(D2D1::Point2F(x, y), r, r);
brush->SetColor(col);
rt->FillEllipse(ellipse, brush);
}
void OnPaint()
{
HRESULT hr = CreateResources();
if (SUCCEEDED(hr))
{
≀
rt->Clear(ColorF(ColorF::Ivory));
DrawCircle(WIDTH / 2, HEIGHT / 2, 30, ColorF(ColorF::Blue));
≀
}
}
実行結果は次のようになります。
今回はちゃんと説明できていなかったのですが、次回、来週の休みの日からはなるべくコードの説明をして、C++自体のことも書いていきたいです。プログラミングは継続なり。