-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSource.cpp
More file actions
656 lines (558 loc) · 27 KB
/
Source.cpp
File metadata and controls
656 lines (558 loc) · 27 KB
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
#include <windows.h>
#include <d3d11.h>
#include <chrono>
#include <string>
#include <cmath> // 三角関数を使うために追加
#include <d3dcompiler.h>
#include <DirectXMath.h>
#include <d2d1.h>
#include <dwrite.h>
#include <cstdio> // swprintf_s を使うため
#include <vector>
#include <Xinput.h>
#pragma comment(lib, "xinput.lib") // 追加: コントローラー入力用
#pragma comment(lib, "d2d1.lib")
#pragma comment(lib, "dwrite.lib")
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "d3dcompiler.lib") // 追加: シェーダーコンパイル用
using namespace DirectX; // 追加: 行列計算用
// --- グローバル変数・定数 ---
const int SCREEN_WIDTH = 1280;
const int SCREEN_HEIGHT = 720;
HWND g_hWnd = nullptr;
ID3D11Device* g_pd3dDevice = nullptr;
ID3D11DeviceContext* g_pImmediateContext = nullptr;
IDXGISwapChain* g_pSwapChain = nullptr;
ID3D11RenderTargetView* g_pRenderTargetView = nullptr;
ID3D11VertexShader* g_pVertexShader = nullptr;
ID3D11PixelShader* g_pPixelShader = nullptr;
ID3D11InputLayout* g_pVertexLayout = nullptr;
ID3D11Buffer* g_pVertexBuffer = nullptr;
ID3D11Buffer* g_pConstantBuffer = nullptr;
ID3D11DepthStencilView* g_pDepthStencilView = nullptr; // 奥行きを管理するバッファ
ID3D11Buffer* g_pTrackVertexBuffer = nullptr; // コース用の頂点バッファ
ID2D1Factory* g_pD2DFactory = nullptr;
ID2D1RenderTarget* g_pD2DRenderTarget = nullptr;
IDWriteFactory* g_pDWriteFactory = nullptr;
IDWriteTextFormat* g_pTextFormat = nullptr;
ID2D1SolidColorBrush* g_pBrushWhite = nullptr;
// 頂点データの構造体(位置と色)
struct SimpleVertex {
XMFLOAT3 Pos;
XMFLOAT4 Color;
};
// GPU(シェーダー)に毎フレーム送るデータ(今回は座標変換用の行列)
struct ConstantBuffer {
XMMATRIX WorldViewProj;
};
// GPUで動かすシンプルなシェーダープログラム(HLSL言語)
const char* g_ShaderCode = R"(
cbuffer ConstantBuffer : register(b0) {
matrix WorldViewProj;
}
struct VS_INPUT {
float3 Pos : POSITION;
float4 Color : COLOR;
};
struct PS_INPUT {
float4 Pos : SV_POSITION;
float4 Color : COLOR;
};
// 頂点シェーダー:3D空間の座標を画面上の座標に変換する
PS_INPUT VS(VS_INPUT input) {
PS_INPUT output;
output.Pos = mul(float4(input.Pos, 1.0f), WorldViewProj);
output.Color = input.Color;
return output;
}
// ピクセルシェーダー:ポリゴンの中を塗りつぶす
float4 PS(PS_INPUT input) : SV_Target {
return input.Color;
}
)";
struct Car {
float x = 0.0f;
float z = 0.0f; // 3D空間の奥行き(2Dならy軸に相当)
float angle = 0.0f; // 車の向き(ラジアン)
float speed = 0.0f; // 現在の速度
// 車の性能パラメータ
const float MAX_SPEED = 50.0f; // 最高速度
const float ACCELERATION = 20.0f; // 加速度
const float FRICTION = 10.0f; // 摩擦(アクセルオフ時の減速)
const float TURN_SPEED = 2.0f; // ハンドルの切れ角の鋭さ
};
Car g_PlayerCar;
// ゲームの状態
enum class GameState {
TITLE,
COUNTDOWN,
RACING,
GOAL
};
GameState g_GameState = GameState::TITLE;
// タイマー関連
auto g_StartTime = std::chrono::high_resolution_clock::now();
float g_CurrentTime = 0.0f;
float g_BestTime = 999.99f; // 初期ベストタイム
struct CourseNode {
float x, z;
float width;
};
// コース全体のデータ
struct CourseData {
std::wstring name;
std::vector<CourseNode> nodes;
ID3D11Buffer* pVertexBuffer = nullptr;
int vertexCount = 0;
};
// グローバル変数に追加
std::vector<CourseData> g_Courses;
int g_CurrentCourseIndex = 0;
// --- 関数宣言 ---
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HRESULT InitD3D(HWND hWnd);
void CleanupD3D();
void Update();
void Render();
void CreateCourseMesh(CourseData& course) {
std::vector<SimpleVertex> vertices;
for (size_t i = 0; i < course.nodes.size() - 1; ++i) {
CourseNode n1 = course.nodes[i];
CourseNode n2 = course.nodes[i + 1];
// 2点間の方向ベクトルを求める
float dx = n2.x - n1.x;
float dz = n2.z - n1.z;
float len = sqrtf(dx * dx + dz * dz);
// 法線ベクトル(道幅方向に広げるためのベクトル)
float nx = -dz / len;
float nz = dx / len;
// 道の左右の頂点を計算
XMFLOAT4 color = (i % 2 == 0) ? XMFLOAT4(0.3f, 0.3f, 0.3f, 1.0f) : XMFLOAT4(0.35f, 0.35f, 0.35f, 1.0f); // 縞模様
// 【修正2】ポリゴンが「上(表)」を向くように、左手前 → 左奥 → 右手前 → 右奥 の順番に修正
vertices.push_back({ XMFLOAT3(n1.x + nx * n1.width, 0.0f, n1.z + nz * n1.width), color }); // 1. n1(手前)の左
vertices.push_back({ XMFLOAT3(n2.x + nx * n2.width, 0.0f, n2.z + nz * n2.width), color }); // 2. n2(奥)の左
vertices.push_back({ XMFLOAT3(n1.x - nx * n1.width, 0.0f, n1.z - nz * n1.width), color }); // 3. n1(手前)の右
vertices.push_back({ XMFLOAT3(n2.x - nx * n2.width, 0.0f, n2.z - nz * n2.width), color }); // 4. n2(奥)の右
}
course.vertexCount = (int)vertices.size();
D3D11_BUFFER_DESC bd = {};
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(SimpleVertex) * course.vertexCount;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
D3D11_SUBRESOURCE_DATA InitData = { vertices.data() };
g_pd3dDevice->CreateBuffer(&bd, &InitData, &course.pVertexBuffer);
}
void SetupCourses() {
// コース1: 直線コース
CourseData c1;
c1.name = L"STRAIGHT WAY";
for (int i = 0; i < 10; i++) c1.nodes.push_back({ 0, (float)i * 200.0f, 20.0f });
CreateCourseMesh(c1);
g_Courses.push_back(c1);
// コース2: オーバル(円形)コース
CourseData c2;
c2.name = L"OVAL CIRCUIT";
float radius = 300.0f;
for (int i = 0; i <= 36; ++i) { // 10度ずつ点を打つ
float angle = XMConvertToRadians((float)i * 10.0f);
c2.nodes.push_back({ sinf(angle) * radius, cosf(angle) * radius, 25.0f });
}
CreateCourseMesh(c2);
g_Courses.push_back(c2);
}
// --- メイン関数 ---
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) {
// 1. ウィンドウクラスの登録
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, L"RacingGame", NULL };
RegisterClassEx(&wc);
// 2. ウィンドウの作成
g_hWnd = CreateWindow(L"RacingGame", L"Time Attack Racing Game Template",
WS_OVERLAPPEDWINDOW, 100, 100, SCREEN_WIDTH, SCREEN_HEIGHT,
NULL, NULL, wc.hInstance, NULL);
// 3. DirectXの初期化
if (FAILED(InitD3D(g_hWnd))) {
CleanupD3D();
UnregisterClass(L"RacingGame", wc.hInstance);
return 0;
}
SetupCourses();
ShowWindow(g_hWnd, nCmdShow);
UpdateWindow(g_hWnd);
// 4. ゲームループ
MSG msg = { 0 };
while (WM_QUIT != msg.message) {
if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else {
// メッセージがない時はゲームの更新と描画を行う
Update();
Render();
}
}
// 5. 終了処理
CleanupD3D();
UnregisterClass(L"RacingGame", wc.hInstance);
return 0;
}
// --- ゲームロジックの更新 ---
void Update() {
// 状態別の更新処理
switch (g_GameState) {
case GameState::TITLE:
{
// コントローラーの接続状態と入力を取得
XINPUT_STATE state;
ZeroMemory(&state, sizeof(XINPUT_STATE));
bool padConnected = (XInputGetState(0, &state) == ERROR_SUCCESS);
// キーボードとパッドの両方の入力を判定
bool isLeft = (GetAsyncKeyState(VK_LEFT) & 0x8000) || (padConnected && (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT));
bool isRight = (GetAsyncKeyState(VK_RIGHT) & 0x8000) || (padConnected && (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT));
bool isStart = (GetAsyncKeyState(VK_SPACE) & 0x8000) || (padConnected && (state.Gamepad.wButtons & (XINPUT_GAMEPAD_A | XINPUT_GAMEPAD_START)));
if (isLeft) {
g_CurrentCourseIndex = (g_CurrentCourseIndex - 1 + g_Courses.size()) % g_Courses.size();
Sleep(200); // 連続入力を防ぐ簡易処理
}
if (isRight) {
g_CurrentCourseIndex = (g_CurrentCourseIndex + 1) % g_Courses.size();
Sleep(200);
}
if (isStart) {
g_PlayerCar.x = g_Courses[g_CurrentCourseIndex].nodes[0].x;
g_PlayerCar.z = g_Courses[g_CurrentCourseIndex].nodes[0].z;
g_PlayerCar.angle = 0;
g_PlayerCar.speed = 0;
g_GameState = GameState::COUNTDOWN;
}
}
break;
case GameState::COUNTDOWN:
// TODO: 3, 2, 1, GO! の演出
g_StartTime = std::chrono::high_resolution_clock::now();
g_GameState = GameState::RACING;
break;
case GameState::RACING:
{
// タイムの計測
auto now = std::chrono::high_resolution_clock::now();
std::chrono::duration<float> elapsed = now - g_StartTime;
g_CurrentTime = elapsed.count();
// 簡易的なフレーム間の時間差(60FPS固定と仮定)
float deltaTime = 1.0f / 60.0f;
// コントローラーの取得
XINPUT_STATE state;
ZeroMemory(&state, sizeof(XINPUT_STATE));
bool padConnected = (XInputGetState(0, &state) == ERROR_SUCCESS);
// 入力フラグ(キーボードとパッドを統合)
bool isAccel = (GetAsyncKeyState(VK_UP) & 0x8000) != 0;
bool isBrake = (GetAsyncKeyState(VK_DOWN) & 0x8000) != 0;
float turnInput = 0.0f; // -1.0(左) ~ 1.0(右) のアナログ値
// キーボードのステアリング入力
if (GetAsyncKeyState(VK_LEFT) & 0x8000) turnInput = -1.0f;
if (GetAsyncKeyState(VK_RIGHT) & 0x8000) turnInput = 1.0f;
// パッドが繋がっている場合の入力上書き
if (padConnected) {
// Aボタン または 右トリガー(RT) でアクセル
if ((state.Gamepad.wButtons & XINPUT_GAMEPAD_A) || state.Gamepad.bRightTrigger > 50) isAccel = true;
// Bボタン または 左トリガー(LT) でブレーキ
if ((state.Gamepad.wButtons & XINPUT_GAMEPAD_B) || state.Gamepad.bLeftTrigger > 50) isBrake = true;
// 左スティックでのステアリング(デッドゾーン:スティックの遊びを考慮)
short thumbLX = state.Gamepad.sThumbLX;
if (abs(thumbLX) > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) {
// スティックの傾きを -1.0 ~ 1.0 の実数に変換(アナログ操作!)
if (thumbLX > 0) {
turnInput = (float)(thumbLX - XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) / (32767 - XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
}
else {
turnInput = (float)(thumbLX + XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) / (32768 - XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
}
}
}
// 1. アクセルとブレーキの適用
if (isAccel) {
g_PlayerCar.speed += g_PlayerCar.ACCELERATION * deltaTime;
}
else if (isBrake) {
g_PlayerCar.speed -= g_PlayerCar.ACCELERATION * deltaTime;
}
else {
// アクセルオフ時の摩擦減速
if (g_PlayerCar.speed > 0) {
g_PlayerCar.speed -= g_PlayerCar.FRICTION * deltaTime;
if (g_PlayerCar.speed < 0) g_PlayerCar.speed = 0;
}
else if (g_PlayerCar.speed < 0) {
g_PlayerCar.speed += g_PlayerCar.FRICTION * deltaTime;
if (g_PlayerCar.speed > 0) g_PlayerCar.speed = 0;
}
}
// 速度制限
if (g_PlayerCar.speed > g_PlayerCar.MAX_SPEED) g_PlayerCar.speed = g_PlayerCar.MAX_SPEED;
if (g_PlayerCar.speed < -g_PlayerCar.MAX_SPEED / 2.0f) g_PlayerCar.speed = -g_PlayerCar.MAX_SPEED / 2.0f;
// 2. ハンドリングの適用(アナログ値 turnInput を掛ける)
if (abs(g_PlayerCar.speed) > 0.1f) {
float turnDirection = (g_PlayerCar.speed > 0) ? 1.0f : -1.0f;
g_PlayerCar.angle += g_PlayerCar.TURN_SPEED * turnInput * turnDirection * deltaTime;
}
// 3. 座標の更新(既存のまま)
g_PlayerCar.x += sinf(g_PlayerCar.angle) * g_PlayerCar.speed * deltaTime;
g_PlayerCar.z += cosf(g_PlayerCar.angle) * g_PlayerCar.speed * deltaTime;
// ※確認用:コンソール出力等で g_PlayerCar の x と z の値を見ると、
// 矢印キーに合わせて車が移動していることがわかります。
}
break;
case GameState::GOAL:
// TODO: リザルト画面の表示、エンターキーで TITLE へ戻る処理
break;
}
}
// --- 描画処理 ---
void Render() {
if (!g_pImmediateContext) return;
// 背景色(状態によって色を変える)
float clearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; // デフォルト: 黒
switch (g_GameState) {
case GameState::TITLE: clearColor[0] = 0.0f; clearColor[1] = 0.0f; clearColor[2] = 0.5f; break; // 青
case GameState::COUNTDOWN: clearColor[0] = 0.5f; clearColor[1] = 0.5f; clearColor[2] = 0.0f; break; // 黄色
case GameState::RACING: clearColor[0] = 0.2f; clearColor[1] = 0.8f; clearColor[2] = 0.2f; break; // 緑 (コースのイメージ)
case GameState::GOAL: clearColor[0] = 0.8f; clearColor[1] = 0.2f; clearColor[2] = 0.2f; break; // 赤
}
// 【修正1】Direct2Dに奪われた描画先と深度テストの設定を、毎フレーム元に戻す
g_pImmediateContext->OMSetRenderTargets(1, &g_pRenderTargetView, g_pDepthStencilView);
g_pImmediateContext->OMSetDepthStencilState(nullptr, 0);
g_pImmediateContext->OMSetBlendState(nullptr, nullptr, 0xFFFFFFFF);
// 画面と深度バッファのクリア
g_pImmediateContext->ClearRenderTargetView(g_pRenderTargetView, clearColor);
g_pImmediateContext->ClearDepthStencilView(g_pDepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);
if (g_GameState == GameState::RACING) {
// --- 1. カメラとプロジェクション(遠近感)の設定 ---
// プロジェクション行列(視野角45度、奥は2000まで見える)
XMMATRIX mProj = XMMatrixPerspectiveFovLH(XM_PIDIV4, (FLOAT)SCREEN_WIDTH / SCREEN_HEIGHT, 0.1f, 2000.0f);
// カメラ(視点)の設定:車の少し後ろ、少し上から追従する
XMVECTOR carPos = XMVectorSet(g_PlayerCar.x, 0.0f, g_PlayerCar.z, 0.0f); // 車の位置
XMVECTOR carDir = XMVectorSet(sinf(g_PlayerCar.angle), 0.0f, cosf(g_PlayerCar.angle), 0.0f); // 車の進行方向
// カメラ位置(車の後ろに15、上に8)
XMVECTOR camOffset = XMVectorSet(-sinf(g_PlayerCar.angle) * 15.0f, 8.0f, -cosf(g_PlayerCar.angle) * 15.0f, 0.0f);
XMVECTOR eyePos = XMVectorAdd(carPos, camOffset);
// カメラの注視点(車の少し前を見る)
XMVECTOR lookAtPos = XMVectorAdd(carPos, XMVectorScale(carDir, 10.0f));
XMVECTOR upVec = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f); // 上方向はY軸
XMMATRIX mView = XMMatrixLookAtLH(eyePos, lookAtPos, upVec);
// 共通のシェーダー設定
UINT stride = sizeof(SimpleVertex);
UINT offset = 0;
g_pImmediateContext->IASetInputLayout(g_pVertexLayout);
g_pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
g_pImmediateContext->VSSetShader(g_pVertexShader, nullptr, 0);
g_pImmediateContext->VSSetConstantBuffers(0, 1, &g_pConstantBuffer);
g_pImmediateContext->PSSetShader(g_pPixelShader, nullptr, 0);
// --- 2. コース(道路)の描画 ---
CourseData& curCourse = g_Courses[g_CurrentCourseIndex];
g_pImmediateContext->IASetVertexBuffers(0, 1, &curCourse.pVertexBuffer, &stride, &offset);
g_pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); // 連続した帯として描画
XMMATRIX mTrackWorld = XMMatrixIdentity();
ConstantBuffer cbTrack;
cbTrack.WorldViewProj = XMMatrixTranspose(mTrackWorld * mView * mProj);
g_pImmediateContext->UpdateSubresource(g_pConstantBuffer, 0, nullptr, &cbTrack, 0, 0);
// セグメントごとに描画
for (int i = 0; i < curCourse.vertexCount; i += 4) {
g_pImmediateContext->Draw(4, i);
}
// --- 3. 車の描画 ---
g_pImmediateContext->IASetVertexBuffers(0, 1, &g_pVertexBuffer, &stride, &offset);
// 車のワールド行列(Y軸で回転し、xとzに移動)
XMMATRIX mCarRotate = XMMatrixRotationY(g_PlayerCar.angle);
XMMATRIX mCarTranslate = XMMatrixTranslation(g_PlayerCar.x, 0.0f, g_PlayerCar.z);
XMMATRIX mCarWorld = mCarRotate * mCarTranslate;
ConstantBuffer cbCar;
cbCar.WorldViewProj = XMMatrixTranspose(mCarWorld * mView * mProj);
g_pImmediateContext->UpdateSubresource(g_pConstantBuffer, 0, nullptr, &cbCar, 0, 0);
g_pImmediateContext->Draw(4, 0);
}
// 3Dの上に2D UIを重ねて描画する
if (g_pD2DRenderTarget) {
g_pD2DRenderTarget->BeginDraw();
// ★★★ タイトル画面用の描画を追加 ★★★
if (g_GameState == GameState::TITLE) {
wchar_t textBuffer[128];
swprintf_s(textBuffer, L"TIME ATTACK RACING\n\nコース: %s\n(<- -> キーで変更)\n\nSPACEキーでスタート!", g_Courses[g_CurrentCourseIndex].name.c_str());
g_pD2DRenderTarget->DrawTextW(
textBuffer, wcslen(textBuffer), g_pTextFormat,
D2D1::RectF(SCREEN_WIDTH / 2.0f - 200.0f, SCREEN_HEIGHT / 2.0f - 100.0f, SCREEN_WIDTH, SCREEN_HEIGHT), g_pBrushWhite
);
}
else if (g_GameState == GameState::RACING) {
wchar_t textBuffer[128];
// スピードメーターの描画 (内部速度をkm/hっぽく見せるために2倍しています)
swprintf_s(textBuffer, L"SPEED: %3.0f km/h", abs(g_PlayerCar.speed * 2.0f));
g_pD2DRenderTarget->DrawTextW(
textBuffer, wcslen(textBuffer), g_pTextFormat,
D2D1::RectF(20.0f, 20.0f, 400.0f, 100.0f), g_pBrushWhite
);
// タイムの描画
swprintf_s(textBuffer, L"TIME: %05.2f", g_CurrentTime);
g_pD2DRenderTarget->DrawTextW(
textBuffer, wcslen(textBuffer), g_pTextFormat,
D2D1::RectF(20.0f, 70.0f, 400.0f, 150.0f), g_pBrushWhite
);
}
else if (g_GameState == GameState::GOAL) {
// ゴール時のテキスト
wchar_t textBuffer[128];
swprintf_s(textBuffer, L"FINISH!\nTIME: %05.2f\nBEST: %05.2f", g_CurrentTime, g_BestTime);
g_pD2DRenderTarget->DrawTextW(
textBuffer, wcslen(textBuffer), g_pTextFormat,
D2D1::RectF(SCREEN_WIDTH / 2.0f - 100.0f, SCREEN_HEIGHT / 2.0f - 50.0f, SCREEN_WIDTH, SCREEN_HEIGHT), g_pBrushWhite
);
}
g_pD2DRenderTarget->EndDraw();
}
// 画面フリップ(バックバッファをフロントバッファに反映)
g_pSwapChain->Present(1, 0); // VSync有効
}
// --- DirectX 11 初期化 ---
HRESULT InitD3D(HWND hWnd) {
DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory(&sd, sizeof(sd));
sd.BufferCount = 1;
sd.BufferDesc.Width = SCREEN_WIDTH;
sd.BufferDesc.Height = SCREEN_HEIGHT;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = hWnd;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = TRUE;
D3D_FEATURE_LEVEL featureLevel;
UINT createDeviceFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; // ★これを追加
HRESULT hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, createDeviceFlags, NULL, 0,
D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pImmediateContext);
if (FAILED(hr)) return hr;
ID3D11Texture2D* pBackBuffer = nullptr;
hr = g_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
if (FAILED(hr)) return hr;
hr = g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &g_pRenderTargetView);
pBackBuffer->Release();
if (FAILED(hr)) return hr;
// 1. シェーダーのコンパイルと作成
ID3DBlob* pVSBlob = nullptr;
D3DCompile(g_ShaderCode, strlen(g_ShaderCode), nullptr, nullptr, nullptr, "VS", "vs_4_0", 0, 0, &pVSBlob, nullptr);
g_pd3dDevice->CreateVertexShader(pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), nullptr, &g_pVertexShader);
ID3DBlob* pPSBlob = nullptr;
D3DCompile(g_ShaderCode, strlen(g_ShaderCode), nullptr, nullptr, nullptr, "PS", "ps_4_0", 0, 0, &pPSBlob, nullptr);
g_pd3dDevice->CreatePixelShader(pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), nullptr, &g_pPixelShader);
pPSBlob->Release();
// 2. 入力レイアウトの定義(頂点データがどういう構造かをGPUに教える)
D3D11_INPUT_ELEMENT_DESC layout[] = {
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
g_pd3dDevice->CreateInputLayout(layout, 2, pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), &g_pVertexLayout);
pVSBlob->Release();
// 3. 車の頂点バッファ(Y軸を高さとして、地面に置く形に変更)
SimpleVertex vertices[] = {
{ XMFLOAT3(-2.0f, 0.5f, 4.0f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) }, // 左前 (赤)
{ XMFLOAT3(2.0f, 0.5f, 4.0f), XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) }, // 右前 (緑)
{ XMFLOAT3(-2.0f, 0.5f, -4.0f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f) }, // 左後 (青)
{ XMFLOAT3(2.0f, 0.5f, -4.0f), XMFLOAT4(1.0f, 1.0f, 0.0f, 1.0f) }, // 右後 (黄)
};
D3D11_BUFFER_DESC bd = {};
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(SimpleVertex) * 4;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
D3D11_SUBRESOURCE_DATA InitData = {};
InitData.pSysMem = vertices;
g_pd3dDevice->CreateBuffer(&bd, &InitData, &g_pVertexBuffer);
// 4. コース(道路)の頂点バッファ(こちらも奥から手前へ)
SimpleVertex trackVertices[] = {
{ XMFLOAT3(-20.0f, 0.0f, 2000.0f), XMFLOAT4(0.3f, 0.3f, 0.3f, 1.0f) }, // 左奥
{ XMFLOAT3(20.0f, 0.0f, 2000.0f), XMFLOAT4(0.3f, 0.3f, 0.3f, 1.0f) }, // 右奥
{ XMFLOAT3(-20.0f, 0.0f, 0.0f), XMFLOAT4(0.3f, 0.3f, 0.3f, 1.0f) }, // 左手前
{ XMFLOAT3(20.0f, 0.0f, 0.0f), XMFLOAT4(0.3f, 0.3f, 0.3f, 1.0f) }, // 右手前
};
bd.ByteWidth = sizeof(SimpleVertex) * 4;
InitData.pSysMem = trackVertices;
g_pd3dDevice->CreateBuffer(&bd, &InitData, &g_pTrackVertexBuffer);
// 5. 定数バッファの作成
bd.ByteWidth = sizeof(ConstantBuffer);
bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
g_pd3dDevice->CreateBuffer(&bd, nullptr, &g_pConstantBuffer);
// 6. 深度バッファ(Zバッファ)の作成(3Dの重なりを正しく描画するため)
D3D11_TEXTURE2D_DESC descDepth = {};
descDepth.Width = SCREEN_WIDTH;
descDepth.Height = SCREEN_HEIGHT;
descDepth.MipLevels = 1;
descDepth.ArraySize = 1;
descDepth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
descDepth.SampleDesc.Count = 1;
descDepth.Usage = D3D11_USAGE_DEFAULT;
descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL;
ID3D11Texture2D* pDepthStencil = nullptr;
g_pd3dDevice->CreateTexture2D(&descDepth, nullptr, &pDepthStencil);
g_pd3dDevice->CreateDepthStencilView(pDepthStencil, nullptr, &g_pDepthStencilView);
pDepthStencil->Release();
// 描画先としてカラーバッファと深度バッファをセット
g_pImmediateContext->OMSetRenderTargets(1, &g_pRenderTargetView, g_pDepthStencilView);
// --- ここが前回抜け落ちていた必須コードです! ---
D3D11_VIEWPORT vp;
vp.Width = (FLOAT)SCREEN_WIDTH;
vp.Height = (FLOAT)SCREEN_HEIGHT;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
g_pImmediateContext->RSSetViewports(1, &vp);
// ----------------------------------------------
// Direct2D / DirectWrite の初期化
D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &g_pD2DFactory);
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&g_pDWriteFactory));
// DirectX11の画面(スワップチェーン)をDirect2Dでも描画できるようにリンクする
IDXGISurface* pSurface = nullptr;
g_pSwapChain->GetBuffer(0, __uuidof(IDXGISurface), (void**)&pSurface);
D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_DEFAULT,
D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED)
);
g_pD2DFactory->CreateDxgiSurfaceRenderTarget(pSurface, &props, &g_pD2DRenderTarget);
pSurface->Release();
// テキスト描画用の白ブラシを作成
g_pD2DRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), &g_pBrushWhite);
// フォントフォーマットの設定(メイリオ、32px)
g_pDWriteFactory->CreateTextFormat(
L"Meiryo", NULL, DWRITE_FONT_WEIGHT_BOLD, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL,
32.0f, L"ja-jp", &g_pTextFormat
);
return S_OK;
}
// --- DirectX クリーンアップ ---
void CleanupD3D() {
if (g_pBrushWhite) g_pBrushWhite->Release();
if (g_pTextFormat) g_pTextFormat->Release();
if (g_pDWriteFactory) g_pDWriteFactory->Release();
if (g_pD2DRenderTarget) g_pD2DRenderTarget->Release();
if (g_pD2DFactory) g_pD2DFactory->Release();
if (g_pDepthStencilView) g_pDepthStencilView->Release();
if (g_pTrackVertexBuffer) g_pTrackVertexBuffer->Release();
if (g_pConstantBuffer) g_pConstantBuffer->Release();
if (g_pVertexBuffer) g_pVertexBuffer->Release();
if (g_pVertexLayout) g_pVertexLayout->Release();
if (g_pVertexShader) g_pVertexShader->Release();
if (g_pPixelShader) g_pPixelShader->Release();
// 既存の解放処理
if (g_pRenderTargetView) g_pRenderTargetView->Release();
if (g_pSwapChain) g_pSwapChain->Release();
if (g_pImmediateContext) g_pImmediateContext->Release();
if (g_pd3dDevice) g_pd3dDevice->Release();
}
// --- ウィンドウプロシージャ ---
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}