ゲームを構成する要素は、
ブロック
ボール
バー
の三つです。
主な処理は、
描画については、
バーを描画する
ボールを描画する。
ブロックを描画する
他の主な処理は、
バーの移動
ボールの移動
ボールの当たり判定
に分けられます。
〇プログラム例
#include <windows.h>
/* ゲームウィンドウの幅 */
#define WND_WIDTH 640
#define WND_HEIGHT 480
VOID StartGame(VOID); /* ゲームデータを初期化する */
VOID EndGame(VOID); /* ゲームを終了する */
VOID Paint(HDC); /* ゲーム全体をペイントする */
VOID PaintBackground(HDC); /* 背景を描画する */
VOID PaintBar(HDC); /* バーを描画する */
VOID PaintBall(HDC); /* ボールを描画する */
VOID PaintBlocks(HDC); /* ブロックを描画する */
VOID MoveBall(VOID); /* ボールを次の位置へ移動させる */
HWND hMainWindow; /* アプリケーションウィンドウのハンドル */
int waitTime = 20;
BOOL isRun = FALSE; /* ゲームが実行されていればTRUE */
HBITMAP hBackBmp = NULL; /* 背景ビットマップ */
#define BLOCK_WIDTH 60 /* ブロックの幅 */
#define BLOCK_HEIGHT 20 /* ブロックの高さ */
/* ブロックの現在の数(blocksポインタが参照する配列の長さ*/
int blockCount;
/*
個々のブロックの情報を提供する構造体。
ブロックの位置とサイズは、
常にBALL_MOVEの倍数になるように設定します。
そうでなければ、当たり判定で不具合が生じます。
*/
typedef struct _tagBlock {
BOOL enable; /* ブロックが有効ならTRUE */
RECT bounds; /* ブロックの位置とサイズ */
} BlockInfo;
BlockInfo* blocks = NULL;
#define BAR_WIDTH 100 /* 操作するバーの幅 */
/* 操作するバーの高さ。
この値はBALL_MOVEの倍数になるように設定してください
*/
#define BAR_HEIGHT 10
POINT barLocation = { 300,400 }; /* バーの位置 */
#define BALL_MOVE 5 /* ボールの移動距離 */
/* TRUEなら正の方向に、FALSEなら負の方向に移動する */
BOOL ballX = TRUE, ballY = FALSE;
#define BALL_WIDTH 10 /* ボールの幅 */
#define BALL_HEIGHT 10 /* ボールの高さ */
POINT ballLocation = { 40,380 }; /*ボールの位置*/
VOID StartGame() {
int i, x, y;
blockCount = 40; /* ブロック数は40 */
blocks = (BlockInfo*)malloc(sizeof(BlockInfo) * blockCount);
x = BALL_WIDTH * 2;
y = BALL_HEIGHT * 2;
for (i = 0; i < blockCount; i++) {
blocks[i].enable = TRUE;
blocks[i].bounds.left = x;
blocks[i].bounds.top = y;
blocks[i].bounds.right = x + BLOCK_WIDTH;
blocks[i].bounds.bottom = y + BLOCK_HEIGHT;
x += BLOCK_WIDTH;
if (x > (WND_WIDTH - BLOCK_WIDTH-(BALL_WIDTH * 2))){
x = BALL_WIDTH * 2;
y += BLOCK_HEIGHT + BALL_MOVE;
}
}
isRun = TRUE;
}
VOID EndGame() {
isRun = FALSE;
if (blocks) {
blockCount = 0;
free(blocks);
blocks = NULL;
}
/* 以下に、追加の終了処理を記述できます。*/
}
VOID Paint(HDC hdc) {
const RECT wndRect = { 0,0,WND_WIDTH, WND_HEIGHT };
FillRect(hdc, &wndRect, GetStockObject(BLACK_BRUSH));
PaintBackground(hdc);
PaintBar(hdc);
PaintBlocks(hdc);
PaintBall(hdc);
}
VOID PaintBackground(HDC hdc) {
if (hBackBmp) {
HDC hBackDC = CreateCompatibleDC(hdc);
SelectObject(hBackDC, hBackBmp);
BitBlt(hdc, 0, 0, WND_WIDTH, WND_HEIGHT, hBackDC, 0, 0, SRCCOPY);
DeleteDC(hBackDC);
DeleteObject(hBackDC);
}
}
VOID PaintBar(HDC hdc) {
SelectObject(hdc, GetStockObject(BLACK_PEN));
SelectObject(hdc, GetStockObject(WHITE_BRUSH));
Rectangle(hdc, barLocation.x, barLocation.y,
barLocation.x + BAR_WIDTH, barLocation.y + BAR_HEIGHT);
}
VOID PaintBall(HDC hdc) {
SelectObject(hdc, GetStockObject(BLACK_PEN));
SelectObject(hdc, GetStockObject(WHITE_BRUSH));
Ellipse(hdc, ballLocation.x, ballLocation.y,
ballLocation.x + BALL_WIDTH, ballLocation.y + BALL_HEIGHT);
}
VOID PaintBlocks(HDC hdc) {
int i;
if (!blocks) return;
for (i = 0; i < blockCount; i++) {
if (!blocks[i].enable)
continue;
Rectangle(hdc, blocks[i].bounds.left, blocks[i].bounds.top,
blocks[i].bounds.right, blocks[i].bounds.bottom);
}
}
VOID MoveBall() {
int i, enableBlockCount;
if (ballX)
ballLocation.x += BALL_MOVE;
else
ballLocation.x -= BALL_MOVE;
if (ballY)
ballLocation.y += BALL_MOVE;
else
ballLocation.y -= BALL_MOVE;
/* 以下、ぼるのバウンド・当たり判定 */
/* ボールが画面の端を超えた場合、跳ね返る */
if (ballLocation.x <= 0)
ballX = TRUE;
else if (ballLocation.x + BALL_WIDTH >= WND_WIDTH)
ballX = FALSE;
if (ballLocation.y <= 0)
ballY = TRUE;
else if ( /*ボールがバーに当たったかどうか*/
ballLocation.y + BALL_HEIGHT >= barLocation.y &&
ballLocation.y <= barLocation.y + BAR_HEIGHT &&
ballLocation.x + BALL_WIDTH >= barLocation.x &&
ballLocation.x <= barLocation.x + BAR_WIDTH)
ballY = FALSE;
/*ボールがブロックに当たったかどうか */
for (i = enableBlockCount = 0; i < blockCount; i++) {
if (blocks == NULL)
break;
if (!blocks[i].enable) {
/* このブロックは既に無効である */
enableBlockCount++;
continue;
}
if (ballLocation.y <= blocks[i].bounds.bottom &&
ballLocation.y + BALL_HEIGHT >= blocks[i].bounds.top) {
if (ballLocation.x + BALL_WIDTH == blocks[i].bounds.left) {
ballX = FALSE;
blocks[i].enable = FALSE;
}
else if (ballLocation.x == blocks[i].bounds.right) {
ballX = TRUE;
blocks[i].enable = FALSE;
}
}
if (ballLocation.x <= blocks[i].bounds.right &&
ballLocation.x + BALL_WIDTH >= blocks[i].bounds.left) {
if (ballLocation.y + BALL_HEIGHT == blocks[i].bounds.top) {
ballY = FALSE;
blocks[i].enable = FALSE;
}
else if (ballLocation.y == blocks[i].bounds.bottom) {
ballY = TRUE;
blocks[i].enable = FALSE;
}
}
}
/* ボールが落ちた、またはブロックがすべて消えた */
if (ballLocation.y > WND_HEIGHT) {
EndGame();
/* 以下に、ゲームオバーの処理を追加できます */
}
else if (enableBlockCount == blockCount) {
EndGame();
/* 以下に、クリアの処理を追加できます */
}
}
DWORD WINAPI ThreadFunc(LPVOID vdParam) {
HWND hWnd = (HWND)vdParam;
while (isRun) {
MoveBall();
InvalidateRect(hWnd, NULL, FALSE);
Sleep(waitTime);
}
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
static DWORD dwThreadID;
/* ダブルバッファリング用のビットマップとデバイスコンテキスト */
static HBITMAP hWndBuffer;
static HDC hBufferDC;
switch (uMsg)
{
case WM_DESTROY:
if (isRun)
EndGame();
DeleteDC(hBufferDC);
DeleteObject(hWndBuffer);
if (hBackBmp)
DeleteObject(hBackBmp);
PostQuitMessage(0);
return 0;
case WM_CREATE:
ShowWindow(hWnd, SW_SHOW);
hMainWindow = hWnd;
hdc=GetDC(hWnd);
hWndBuffer = CreateCompatibleBitmap(hdc, WND_WIDTH, WND_HEIGHT);
hBufferDC = CreateCompatibleDC(hdc);
SelectObject(hBufferDC, hWndBuffer);
ReleaseDC(hWnd, hdc);
hBackBmp = LoadImage(NULL, TEXT("back.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
StartGame();
CreateThread(NULL, 0, ThreadFunc, (LPVOID)hWnd, 0, &dwThreadID);
return 0;
case WM_MOUSEMOVE:
barLocation.x = LOWORD(lParam) - (BAR_WIDTH / 2);
if (barLocation.x < 0)
barLocation.x = 0;
else if (barLocation.x + BAR_WIDTH > WND_WIDTH) {
barLocation.x = WND_WIDTH - BAR_WIDTH;
}
InvalidateRect(hWnd, NULL, FALSE);
return 0;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
Paint(hBufferDC);
BitBlt(hdc, 0, 0, WND_WIDTH, WND_HEIGHT, hBufferDC, 0, 0, SRCCOPY);
EndPaint(hWnd, &ps);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
int APIENTRY wWinMain(
_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR ipCmdLine,
_In_ int nCmdShow
) {
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 = NULL;
wcex.hCursor = NULL;
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = TEXT("TestWindow");
wcex.hIconSm = NULL;
RegisterClassExW(&wcex);
hMainWindow = CreateWindowEx(0UL, TEXT("TestWindow"), TEXT("日本"), WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, 640, 480, NULL, NULL, hInstance, NULL);
if (hMainWindow == NULL)
{
return FALSE;
}
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0) {
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
※参考 Windowsゲームプログラミング
〇実行結果
これで一通り、Windowsゲームプログラミングを読むことができました。
古い本ですが、参考になるところは多かったと思います。