コンテンツ ディレクトリ:
1. Win32 プログラムの作成
2. ウィンドウの作成
3.ゲームループ紹介
4. メッセージ処理
5. ゲームプログラムのフレームワーク
1. Win32 プログラムの作成
まず、VS でプロジェクトを作成する方法を教えます。
1. VS を開いた後、ページの右側にある をクリックして、新しいプロジェクトを作成します。
2. [Windows デスクトップ ウィザード] を選択し、次の手順に進みます
3. プロジェクト名と場所を入力し、以下を作成します。
4. この時点でポップアップ ウィンドウが表示されます。次の図に従って設定する必要があります。[OK] をクリックします。
5. プロジェクト ファイルを入力したので、コードを記述するファイルを作成する必要があります。
このとき、ウィンドウ内にフローティング ウィンドウ、ソリューション エクスプローラーが表示されます。
上部のビューを見つけて、中のソリューション エクスプローラーをクリックします。
ソースファイルを右クリック --- ソースファイル --- 追加 --- 新しいアイテム
このとき、次のポップアップ ウィンドウが表示されます。
右側の [Visual C++] をクリックし、次に [C++ ファイル] をクリックする必要があります。下の名前を変更して、[追加] をクリックします。
以上で、プロジェクトの作成は終了です。
この時点で、次のコードをファイルに直接貼り付け、デバッガーをクリックして実行できます。
#include<Windows.h> int WINAPI WinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) { OutputDebugString(L"hello world!"); return 0; }
上記のプログラムを作成すると、プログラムが実行されて終了することがわかります。この時点で、古典的な helloworld に到達しました。どうして見てくれなかったの?
ここに helloworld があります。これは、プログラムが正常に実行されたことを意味します。
上記のコードの場合:
最初に WinMain について話しましょう: c/c++ コンソール (つまり、最初に c++/c を学習するときの黒いフレーム) プログラムにはメイン関数があり、これが Win32 プログラムです。そのメイン関数は WinMain です。先ほどのメインと同様、プログラムの入り口です。
さらに、彼にはいくつかのパラメーターがあります. これらに触れていない場合は、それらをコピーして終了できます. まだ混乱する可能性があるため、ここでは説明しません. 知らない概念がたくさんあります.初心者の頃 習ってもすぐに忘れてしまうので、とりあえずコピー!
以前の c++/c プログラムには、プログラムの出力を支援するために printf や cout のようなものがありましたが、Win32 プログラムではコンソール ウィンドウがないためなくなりました。それでは、いくつかのプログラムでデータを出力して印刷する方法は? ここの OutputDebugString は問題ありません。
文字列の前に L がある理由は、文字タイプの一致の問題のためです。追加するだけで終わりです。これらの副次的な内容は掘り下げません。
2. ウィンドウの作成
まず、このウィンドウの作成には、主に Win32 プログラミングの知識が必要です。
したがって、このコードについては、あまり詳しく説明しません。コピーして貼り付けて、削除するだけです。なぜ?Win32プログラミングに触れたことのない方は、私が言ったとしても、少し混乱するかもしれませんし、しばらく覚えていないかもしれません.ウィンドウを作成するプロセスを把握するだけで十分です。
画面にウィンドウを表示するには、次の 3 つの手順があります。
1.ウィンドウクラスを登録する
2. ウィンドウを作成する
3. ウィンドウの移動、表示、更新を 3 回行う
#include<Windows.h>
#define WINDOWTITLE L"致我们永不熄灭的游戏开发梦想~"
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
int WINAPI WinMain(
_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine,
_In_ int nCmdShow)
{
//注册窗口类
WNDCLASSEX wndClass = { 0 };
wndClass.cbSize = sizeof(WNDCLASSEX);
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = DefWindowProc;
wndClass.hInstance = hInstance;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hIcon = nullptr;
wndClass.hCursor = nullptr;
wndClass.hbrBackground = nullptr;
wndClass.lpszMenuName = nullptr;
wndClass.lpszClassName = L"ForTheDreamOfGameDevelop";
if (!RegisterClassEx(&wndClass))
{
MessageBox(0, L"RegisterClass Failed.", 0, 0);
return false;
}
//创建窗口
HWND hwnd = CreateWindow(L"ForTheDreamOfGameDevelop", WINDOWTITLE,
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH,
WINDOW_HEIGHT, NULL, NULL, hInstance, NULL);
if (!hwnd)
{
MessageBox(0, L"CreateWindow Failed.", 0, 0);
return false;
}
//窗口移动,显示,更新
MoveWindow(hwnd, 250, 80, WINDOW_WIDTH, WINDOW_HEIGHT, true);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
return 0;
}
コード分析:
登録の場合、彼はフォームの入力、つまり、通常何かを登録するのと同じように、関連する情報を入力するのと同じように、構造に入力するだけです!
ここで理解しておかなければならない点があります.ウィンドウクラスを登録しているので、クラス名を指定する必要があります.lpszClassName. ここでは、ゲーム開発の夢のためにクラス名を L"ForTheDreamOfGameDevelop" に設定します (私が初心者の頃に彼のチュートリアルに書かれていたクラス名 Qianmo から、引き続き使用します)。
次に、RegisterClassEx 関数を呼び出して、入力された登録情報を送信できます。彼のパラメーターは、上で入力したもののアドレスです。ここには MessageBox があり、後で説明します!
ウィンドウの作成: CreateWindow 呼び出し。最初の 4 つのパラメーターのみ。最初のパラメーターは登録したクラス名、2 番目のパラメーターはウィンドウの名前です。ここではマクロ WINDOWTITLE を定義します。ウィンドウの幅と高さは後で指定しますが、これもマクロ WINDOW_WIDTH、WINDOW_HEIGHT です。他の人は今のところ気にしないでください。
ps: MessageBox について話しましょう. 今知っておくべきことは: 2 番目のパラメーターは、ポップアップ ウィンドウに出力するコンテンツを書き込み、残りはそのままにしておくことです。また、この操作によってプログラムがブロックされること、つまり、このポップアップ ウィンドウのボタンをクリックするまでコードが実行されなくなることも知っておく必要があります。
ウィンドウの移動, 表示, 更新 : それぞれ 3 つの関数. ウィンドウを作成したら, MoveWindow でその位置を調整できます. 2 番目と 3 番目のパラメータはウィンドウの右上隅の座標です (座標軸は左上です)画面の隅を原点として)。次に、ShowWindow がウィンドウを表示します。最後の 1 つはウィンドウを更新することで、通常は ShowWindow が続きます。
上記のプログラムを実行すると、ウィンドウが点滅することがわかります。そもそも彼の理由とは?コードを見ればわかりますが、ウィンドウが表示された後、すぐにコードが実行される、つまりプログラムが実行されます. もちろん、ウィンドウは消えるはずです.
では、彼を止めたいと思ったらどうしますか?
まずメソッドの話ですが、ウィンドウを表示したい場合は、表示を制御した後は、コードを実行しなくてもかまいません。なんと、今お話ししたMessageBoxにはこんな機能が!プログラムの最後に 0; を返す前に、ShowWindow の後に MessageBox(0, L"i am a box ", 0, 0); を彼に追加します。
上記の方法で、MessageBox のブロックがどのように機能するかを確認したいと思います。ゲームプログラムは実際にどのように動作しますか? ——これが私たちのゲームループがやろうとしていることです。以下の詳細な紹介を見てみましょう!
3.ゲームループ紹介
サイクルについて話す前に、いくつかの予兆について話しましょう: 私たちのゲームのダイナミックな絵はどのように実現されるのでしょうか? まず第一に、コアとなるアイデアは: 静止画像が 1 枚ずつ一貫して表示されるように、短い時間で連続して静止画像を表示することです. 実際、各静止画像は、私たちがしばしば写真の各フレームと呼ぶものです. 次々とフレームが描画されるので、描画操作をループに入れて、次から次へとフレームを描画し、ループし続ける必要があります。
事前にすべてのフレームを準備して、それをプログラムで表示することはできません。それは映画を見るようなものではないでしょうか。
ゲーム表示のロジックは?まず、ゲームで描画するものは、データ+いくつかの絵やその他のリソースファイルなどです。例えば、画面上に三角形を描く場合、その3点の位置などの情報を指定する必要があり、ファイルは画面に貼り付けられ、最後にプログラムが設定したデータ リソースを描画し、画面に表示するのに役立ちます。
次に、ゲームサイクルのロジックは次のとおりです。最初に、描画するデータと画像をいくつか作成し、次にこのフレームを描画しました。次のフレームになると、2 つのフレーム間で変更がある場合とない場合があります. まず、前のフレームのデータと写真をメモリに保持します.三角形の場合、データに対して足し算や引き算などの操作を実行するだけで、移動後に新しい座標を取得できます。変更が行われると、それらを画面に描画できます。どのデータにも変更がない場合、フレームが前のフレームとまったく同じであることを意味します。というわけで、ゲームの実行中に画面イメージに現れる 2 つの状況を説明しました. 画面上のものは動くが、画面上のものは前のフレームと同じで動かない.
たくさん言ったので、理解できるかもしれませんが、あまり感じません. 小さな例でテストしてみましょう:
HDC hdc = GetDC(hwnd);
float posX = 30.0f;
while (1)
{
posX += 0.001f;
TextOut(hdc, posX, 150, L"hello", 5);
}
ReleaseDC(hwnd, hdc);
動画効果を実現するには、前のコードが 0 を返す前に上記の行を追加するだけで済みます。
上記のコードで主に行うことは、TextOut を使用してテキストの文字列を書き込み、このポイントの位置データをフレームごとに変更して、ゲームの動的な画像を実現できるようにすることです。 、それは常に同じ座標になり、helloは常に同じ位置に書かれ、静止しているように見えます。(ここで Get DC をコードに追加することについて心配する必要はありません。テキスト出力の x 座標を示す Text Out パラメーターに posX があることだけを理解する必要があります。次に、出力の内容とposX はループ内で変化し続けます。)
数行のコードを追加して自分で実行してみると、問題があることがわかります。つまり、マウスを置いて回転し続けると、右上の X をクリックする方法がありません。閉めます。
VS のみ:
デバッグを終了してプログラムを閉じます。
理由は何ですか?つまり、私たちが書いたプログラムは常にループで実行され、マウス情報をチェックしていません! つまり、マウスを右上隅の X に移動してクリックします.応答が必要な場合は、マウスのクリック動作を確認する必要があります。率直に言えば、プログラムの外部から何らかの情報がプログラムに送信され、正しい終了効果を得るためにこの情報を処理する必要があります。
4. メッセージ処理
1.メッセージループ
ここでポイントから始めましょう. マウスやキーボードからのメッセージなど、プログラムの外部からのメッセージを処理する場合は、まずこのメッセージを受信する必要があります. ここで、メッセージは自動的にあなたの写真を撮って送信しません.あなたは手紙ですが、メッセージはキーボードから入力され、オペレーティングシステムを介して情報をバケットに入れます。プログラムはそれを見るためにバケットに行く必要があります!
私が言いたいのは、情報があるかどうかを確認するには、バケットに移動する必要があるということです. 次に、問題は、操作をバケットのどこに配置するかです。
プログラムはいつでも何らかの外部情報を受け取る可能性があるため、答えは明らかです。そのため、各ループ内のバケットを調べて、着信レターがあるかどうかを確認する必要があります。
以下のコードを見てください。
MSG msg = { 0 };
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
ここで情報の構造体であるMSG変数を定義するのですが、初期化されていないと警告がでます~。次に、ループの条件は、前回受信したメッセージの内容が WM_QUIT、つまり終了メッセージであるかどうかであり、そうであればループを終了するか、ループに入る。(なぜ前回だったのか、今回は見ていないのに!)
ここで 3 つの関数を見てみましょう。
PeekMessage の機能は、バケットに情報があるかどうかを確認することです。ある場合は、その情報を最初のパラメーターの位置に配置し、ゼロ以外の値を返します。一目でバケットに情報がない場合、彼は 0 を返します。この関数は、バケットに情報があるかどうかに関係なく、バケットを覗くとすぐに返されることに注意してください。他のパラメータはさておき、まずは彼のコアをつかんでください。
以下の 2 つの機能について、ここではまずメッセージ処理の仕組みについて説明します。
まず写真をどうぞ:
キーボード入力を例に取りましょう。たとえば、特定のキーを押します。OSはこれらのハードウェアを制御するため、ハードウェアが入力されると、まず入力情報がOSに渡されます。次に、オペレーティング システムは情報をメッセージ キューに入れます。メッセージ キューは、先ほど口頭で説明したバケットの概念です。次に、ゲーム プログラムに PeekMessage があります。このバケツに来て、メッセージがあるかどうかを確認します。
情報がある場合、私たちのコードによれば、 if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) 彼は成功を判断し、操作 TranslateMessage を実行します. このとき、受信メッセージは前処理されます. 彼は翻訳します.情報を処理します (または、情報の一部を無視するなど)。情報が処理される場合、その情報は処理のためにオペレーティング システムに返される必要があります。
このステップが DispatchMessage です。次に、オペレーティング システムがそれを処理する方法は、情報の処理方法を記録するウィンドウ処理関数と呼ばれるものを呼び出すことです。これにより、各情報の処理が完了します。
メッセージ処理の全体的なコード フローを確認します。まず、前のメッセージが exit であるかどうかを確認します。前回 exit メッセージを受信した場合は、今回はループに入らないでください。そうでない場合はループに入ります。入力後、ニュースがあるかどうかを確認し、ニュースがない場合は、現在のサイクルが終了し、次のラウンドが続行されます。情報がある場合は、それを翻訳して処理し、オペレーティング システムに渡して処理します。
メッセージの扱い方は?ゲーム内で行うアクションを指定するのは私ではないでしょうか? これを制御するには、どこに行けばよいでしょうか。——それが、今回ご紹介したいメッセージ処理機能です。
2. メッセージ処理機能
以前はメッセージ処理機能を指定していなかったのに、なぜ右上の×を閉じたときにウィンドウを閉じてしまったのでしょうか。これは、システムにデフォルトのメッセージ処理機能があり、デフォルトの処理方法で情報を処理するのに役立つためです。
以前ウィンドウクラスを登録したときに、このような項目を記入しました. まず、DefWindowProc はデフォルトの Window プロシージャです。default はデフォルトです。ここでは、未指定ではなく、デフォルトのメッセージ処理を選択しました。
では、率先してメッセージの処理方法を決定します。つまり、メッセージ処理関数を自分で作成します。
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
DestroyWindow(hwnd);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
上記はカスタム メッセージ処理関数です. まず, この関数がオペレーティング システムによって呼び出されることを知っておく必要があります. この関数は情報の特定の内容を確実に渡します. たとえば, メッセージ, これは型です. of message、および wParam、lParam の両方がメッセージの特定のコンテンツです。
内部でメッセージを判断する必要があるだけです。最初にメッセージのタイプを判断するスイッチを入れ、メッセージが WM_KEYDOWN タイプであるかどうかを確認し、それがキーボード プレス メッセージであるかどうかを示し、次に、押されたキーが VK_ESCAPE であるかどうかを確認します。キーボードの左上隅にあるキーである Esc キー。このキーの場合、ウィンドウを破棄する DestroyWindow 関数を実行します。
次に、ここにデフォルトがあり、これは、処理する情報のタイプを定義していないことを意味します.たとえば、ここではマウス情報を処理していません. 次に、デフォルトのブランチが実行されます。DefWindowProc を呼び出します。これは少しおなじみですが、前に書いたデフォルトの処理関数です。
なぜなら、プログラムが実行されると、プログラムの動作に関連する多くの情報を受け取るからです. これらの情報は体系的です. それらを管理する必要はなく、自分で書いて処理する必要もありません.デフォルトの処理に任せることができるので、常にデフォルトのままにしているので書きます。
つまり、処理したい情報の処理方法をカスタマイズし、残りはシステムに任せます。
では、メッセージ処理関数はどのように機能するのでしょうか? クラスを登録するときに、独自のウィンドウ処理関数を記述するだけです。
次に、カスタム関数の実装を書き出すと、正常に実行できます! 実行後、Esc キーを押すと、ウィンドウ全体が破棄されます. 想像力を働かせてください.キーボードを使って、画面上のテキストの動きを制御できますか? これは後で実装します。
ここで、質問について考えてみましょう。つまり、ゲーム ループを作成し、次にメッセージ ループを作成しました。それらの間の関係は何ですか?
5. ゲームプログラムのフレームワーク
まず第一に, ゲームループは、ゲーム中に各フレームが常に描画される場所であるべきです. メッセージループをゲームループに入れるだけでよいので、各フレームを描画するときに、現在のメッセージが存在するかどうかを最初に判断します.受信した後、対応する処理を行い、メッセージを処理した後、描画作業を行います。
フォントの動きを制御するキーボードを実装しましょう。
#include<Windows.h>
#define WINDOWTITLE L"致我们永不熄灭的游戏开发梦想~"
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
//----------------begin-----------------
float g_posX = 0;
float g_posY = 0;
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
DestroyWindow(hwnd);
else if (wParam == VK_LEFT)
g_posX -= 1.0f;
else if (wParam == VK_RIGHT)
g_posX += 1.0f;
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
//----------------end-------------------
int WINAPI WinMain(
_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine,
_In_ int nCmdShow)
{
WNDCLASSEX wndClass = { 0 };
wndClass.cbSize = sizeof(WNDCLASSEX);
wndClass.style = CS_HREDRAW | CS_VREDRAW;
//----------------begin-----------------
wndClass.lpfnWndProc = WndProc;
//----------------end-------------------
wndClass.hInstance = hInstance;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hIcon = nullptr;
wndClass.hCursor = nullptr;
wndClass.hbrBackground = nullptr;
wndClass.lpszMenuName = nullptr;
wndClass.lpszClassName = L"ForTheDreamOfGameDevelop";
if (!RegisterClassEx(&wndClass))
{
MessageBox(0, L"RegisterClass Failed.", 0, 0);
return false;
}
HWND hwnd = CreateWindow(L"ForTheDreamOfGameDevelop", WINDOWTITLE,
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH,
WINDOW_HEIGHT, NULL, NULL, hInstance, NULL);
if (!hwnd)
{
MessageBox(0, L"CreateWindow Failed.", 0, 0);
return false;
}
MoveWindow(hwnd, 250, 80, WINDOW_WIDTH, WINDOW_HEIGHT, true);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
//----------------begin-----------------
g_posX = 400.0f;
g_posY = 1.0f;
MSG msg = { 0 };
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
g_posY += 0.001f;
HDC hdc = GetDC(hwnd);
TextOut(hdc, g_posX, g_posY, L"hello", 5);
ReleaseDC(hwnd, hdc);
}
}
//----------------end-------------------
return 0;
}
上記のコードは、フォントが y 方向に自動的に下に移動することを認識しており、→ または ← を押してフォントを x 方向に移動できます。同時に、上記のコードの以前の変更部分と比較して、 //----begin/end----------- を追加しました---- ---- このようなプロンプトで、変更する場所がすぐに見つかることを願っています。
変更点が 1 つあります。まず、フォント描画の位置座標として 2 つのグローバル変数を作成し、プログラムでそれらを初期化してから、ループ内で適宜更新し、最後に描画時に使用します。ここにこれらを書く目的は、ゲーム全体のフレームワークを構築するのに役立つことです!
どのように言って?データの初期化 - データの更新 - データに従って描画を完了する - 次の 2 つのループ
これがゲームのフレームワーク構造です. 次に、コードを変更します. 初期化、更新、および描画操作を個別に関数にパッケージ化して、プログラムが実行された後もコード ロジックが明確であることをプログラムが保証できるようにします.多くのコード。
//-----------------------------------【头文件包含】-----------------------------------
#include<Windows.h>
//------------------------------------------------------------------------------------
//-----------------------------------【宏定义部分】-----------------------------------
#define WINDOWTITLE L"致我们永不熄灭的游戏开发梦想~"
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
//------------------------------------------------------------------------------------
//-----------------------------------【全局变量部分】-----------------------------------
float g_posX = 0;
float g_posY = 0;
HINSTANCE g_hInstance = 0;
int g_nCmdShow = 0;
HWND g_hwnd = 0;
//------------------------------------------------------------------------------------
//-----------------------------------【函数声明部分】-----------------------------------
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
void InitWindow();//初始化窗口
void Init();//初始化程序
void UpdateScene();//更新绘制的数据
void DrawScene();//进行绘制操作
void Run();
//------------------------------------------------------------------------------------
//-----------------------------------【WinMain函数】-----------------------------------
//Win32程序的入口
//------------------------------------------------------------------------------------
int WINAPI WinMain(
_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine,
_In_ int nCmdShow)
{
g_hInstance = hInstance;
g_nCmdShow = nCmdShow;
Init();
Run();
return 0;
}
//-----------------------------------【初始化创建窗口】-----------------------------------
// 初始化窗口
//--------------------------------------------------------------------------------------
void InitWindow()
{
WNDCLASSEX wndClass = { 0 };
wndClass.cbSize = sizeof(WNDCLASSEX);
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WndProc;
wndClass.hInstance = g_hInstance;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hIcon = nullptr;
wndClass.hCursor = nullptr;
wndClass.hbrBackground = nullptr;
wndClass.lpszMenuName = nullptr;
wndClass.lpszClassName = L"ForTheDreamOfGameDevelop";
if (!RegisterClassEx(&wndClass))
{
MessageBox(0, L"RegisterClass Failed.", 0, 0);
return;
}
g_hwnd = CreateWindow(L"ForTheDreamOfGameDevelop", WINDOWTITLE,
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH,
WINDOW_HEIGHT, NULL, NULL, g_hInstance, NULL);
if (!g_hwnd)
{
MessageBox(0, L"CreateWindow Failed.", 0, 0);
return;
}
MoveWindow(g_hwnd, 250, 80, WINDOW_WIDTH, WINDOW_HEIGHT, true);
ShowWindow(g_hwnd, g_nCmdShow);
UpdateWindow(g_hwnd);
}
//-----------------------------------【窗口处理函数】-----------------------------------
// 处理外来信息
//--------------------------------------------------------------------------------------
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
DestroyWindow(hwnd);
else if (wParam == VK_LEFT)
g_posX -= 1.0f;
else if (wParam == VK_RIGHT)
g_posX += 1.0f;
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
//-----------------------------------【程序初始化】-----------------------------------
// 初始化窗口和数据等
//--------------------------------------------------------------------------------------
void Init()
{
InitWindow();
g_posX = 400.0f;
g_posY = 1.0f;
}
//-----------------------------------【更新绘制的数据】-----------------------------------
// 处理外来信息
//--------------------------------------------------------------------------------------
void UpdateScene()
{
g_posY += 0.001f;
}
//-----------------------------------【进行绘制】-----------------------------------
// 处理外来信息
//--------------------------------------------------------------------------------------
void DrawScene()
{
HDC hdc = GetDC(g_hwnd);
TextOut(hdc, g_posX, g_posY, L"hello", 5);
ReleaseDC(g_hwnd, hdc);
}
//-----------------------------------【游戏运行函数】-----------------------------------
// 游戏循环框架封装在其中
//--------------------------------------------------------------------------------------
void Run()
{
MSG msg = { 0 };
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
UpdateScene();
DrawScene();
}
}
}
ここでは、いくつかの関数をカプセル化します。宣言部分、関数、および構造からそれらを確認できます。
Init には、InitWindow を呼び出してウィンドウを初期化することや、いくつかのデータの初期化など、プログラム内のすべての初期化が含まれます。
UpdateScene と DrawScene は、データの更新と描画操作です。
この時点で、WinMain のメイン関数を見ることができますが、より明確ですか? もちろん, あなたは私がプログラムにいくつかのグローバル変数を導入したことに気付くでしょう. グローバル変数を導入する操作は、それらを関数にカプセル化するためであるため、いくつかの変数は直接使用してはならず、ここでは、使用する必要のあるパラメータをグローバル変数にコピーして、すべての関数に直接アクセスできるようにして、パラメータの受け渡しを回避します. ここですべてをグローバル変数として記述するのは良くありません.
しかし、将来的には、上記のものをクラスにカプセル化し、これらの変数をメンバー変数として使用します. 現時点では、関数はパラメーターを渡さずに直接使用でき、データはグローバルに公開されません.
ここでは主にゲーム本体を紹介するので、とりあえずこんな感じで書きます。
最後に書く
以上で本日の説明は終わりです。ここで少しお聞きしたいことがあります。つまり、上記のフレームワークを説明するときに、多くの関数のパラメーターが何を意味し、どのように記述するかなど、言及しなかった詳細がたくさんあります。これらは後で使用するもので、詳しく紹介します。
この節の内容については、コードの枠組み全体を十分に理解していただければと思いますが、後で書くコードはこの枠組みを埋めるためのものなので、埋めた内容をBではなくAの位置に書くのはなぜですか?フレームワークの。
下の写真の文は、私が初心者だったとき、Qian Mo (Mao Xingyun) がチュートリアルで言及したものです. 私は彼に写真の p を与えました. ここでも彼にあげます. 私たちのゲームの夢を発展させましょう.がんばれ!——ゲーム開発という終わりのない夢へ!
(この記事には間違いや脱落が避けられません。見つけた場合は、修正していただきありがとうございます。この記事を真剣に読んだ場合は、コメントを残して、あなたの気持ちを書き留めてください。これが私の動機です。書き続けてください!ありがとう!)