使用C/C++制作简单的俄罗斯方块 二

一、基本流程

1、初始化游戏资源

1)、游戏所需要的资源

(1)hwnd:用上一节我们定义的句柄。
(2)HDC类型的句柄,因此我们定义全局变量g_hdc,它需要与窗口绑定一起,我们可以使用GetDC(hwnd)来绑定,可以这么写。

HDC g_hdc;
g_hdc = GetDC(hwnd);

ps:浅一点来说两个都是句柄,但是hwnd是来表示窗口的,HDC更多的是关于图像绘制的。
此外,模板并未使用GetDC,它在窗口过程函数WndProc使用的是BeginPaintEndPaint获取设备句柄,但是GetDC更简单,我建议使用这种方法。

模板自带:

PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: 在此处添加使用 hdc 的任何绘图代码...
EndPaint(hWnd, &ps);
我们用一个专门的函数实现初始化资源
BOOL Game_Init(HWND hwnd) {
	g_hdc = GetDC(hwnd);
	/*开始绘制*/
	Game_Paint(hwnd);
	return true;
}

其中Game_Paint也是我们自己创造的绘图函数,待会会实现。
另外,为了以防万一,Game_Init函数用的是布尔型,用来检测初始化是否失败,我们可以在主函数中这么写:

    // 执行应用程序初始化:
	hInst = hInstance; // 将实例句柄存储在全局变量中

	HWND hwnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT, WIND_WIDTH, WIND_HEIGHT, nullptr, nullptr, hInstance, nullptr);
	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	if (!hwnd)
	{
		return FALSE;
	}
	if (!Game_Init(hwnd)) {
		MessageBox(hwnd, L"ERROR", L"消息窗口", 0);
		return FALSE;
	}
    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MODSDEMO));

其中MessageBox是消息框函数,定义如下:

MessageBox( 
	HWND hWnd, 			//句柄
	LPCTSTR lpText, 	//显示的文本
	LPCTSTR lpCaption, 	//消息框的标题,也可取各类参数
	UINT uType			//指定对话框的内容和行为,各类参数可以查阅资料
)

使用如图:

在这里插入图片描述

2、绘制游戏

1)、Rectangle函数

我们都知道,俄罗斯方块,少不了正方形,恰好C++内置函数Rectangle能够实现这个功能,此外还有圆,椭圆等函数,读者可自行查找定义如下:

WINGDIAPI BOOL WINAPI Rectangle(
_In_ HDC hdc, 		//句柄
_In_ int left, 		//矩形左上角x坐标
_In_ int top, 		//矩形左上角x坐标
_In_ int right, 	//矩形右上角x坐标
_In_ int bottom		//矩形右上角y坐标
);

假设我们要在(0,0)位置绘制一个边长25的矩形,我们可以这么写:

Rectangle(g_hdc,0,0,25,25);

2)、windows的2D动画技术

我们都指知道,大部分的2D其实都是一个NPC准备多张图片,然后快速播放,让我们以为它是在‘动’,其实就是几张图片循环播放而已。
因此我们也能在此使用定时器SetTimer来控制播放的速度,定义如下:

SetTimer(
    _In_opt_ HWND hWnd,		//句柄
    _In_ UINT_PTR nIDEvent,//定时器标识符,可以随便选数字
    _In_ UINT uElapse,		//多久绘制一次,单位ms
    _In_opt_ TIMERPROC lpTimerFunc);//回调函数,我们不需要,设置为NULL

我们可以这么使用它:

SetTimer(hwnd, 1, 500, NULL);//500ms绘制一次

使用完之后我们要销毁它:

KillTimer(hwnd, 1);//1就是创造定时器的标识符

现在我们就要修改窗口过程函数了,让它接受信息的时候500ms重新绘制一下窗口:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_TIMER:
		Game_Paint(hwnd);
		break;
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 分析菜单选择:
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

你可以测试一下:

VOID Game_Paint(HWND hwnd) {
	srand((unsigned)time(NULL));//随机种子
	Rectangle(g_hdc, rand() % WIND_WIDTH, rand() % WIND_HEIGHT, rand() % WIND_WIDTH + 50, rand() % WIND_HEIGHT+50);
	return;
}

过程函数不变
ps:使用随机函数的时候记得加上头文件time.h,因为我们要使用srand((unsigned)time(NULL));来更新随机数种子,不然都是伪随机数,就是每次都是一样的数
运行如图
在这里插入图片描述

此外,你还可以使用画刷HBRUSH改变矩形颜色,需要和下面所讲的函数一起使用:

HBRUSH Brush = CreateSolidBrush(RGB(rand() % 256, rand() % 256, rand() % 256));
//注意,参数是RGB三原色,每个取值为0~255.

3)、SelectObject函数

SelectObject 把一个对象(位图、画笔、画刷等)选入指定的设备描述表。新的对象代替同一类型的老对象,绘图的时候我们必须要用此函数,一般来说我们都是这样使用的:

HGDIOBJ  SelectObject(
   HDC  hdc,          
   HGDIOBJ  hgdiobj   // 对象,比如上述的Brush
);

使用方法:

VOID Game_Paint(HWND hwnd) {
	srand((unsigned)time(NULL));//随机种子
	HBRUSH Brush = CreateSolidBrush(RGB(rand() % 256, rand() % 256, rand() % 256));
	SelectObject(g_hdc, Brush);
	Rectangle(g_hdc, rand() % WIND_WIDTH, rand() % WIND_HEIGHT, rand() % WIND_WIDTH + 50, rand() % WIND_HEIGHT+50);
	return;
}

运行如下:

在这里插入图片描述

此外,我们绘制的模板一般都是这么写:
1.创建位图HBITMAP,画刷等对象HBRUSH obj
2.SelectObject(句柄,obj);
3.绘制.。
All Code:

#include "framework.h"
#include "ModsDemo.h"
#include <time.h>
#define  WIND_WIDTH 800
#define  WIND_HEIGHT 680
#define MAX_LOADSTRING 100

// 全局变量:
HINSTANCE hInst;                                // 当前实例
WCHAR szTitle[MAX_LOADSTRING];                  // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口类名
HDC g_hdc, g_mdc;				//句柄

// 此代码模块中包含的函数的前向声明:
ATOM                MyRegisterClass(HINSTANCE hInstance);
LRESULT CALLBACK	WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);
BOOL Game_Init(HWND hwnd);  //游戏资源初始化
VOID Game_Paint(HWND hwnd); //绘制游戏
VOID Game_Clean(HWND hwnd); //释放资源

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR    lpCmdLine,
    _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: 在此处放置代码。

    // 初始化全局字符串
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_MODSDEMO, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // 执行应用程序初始化:
    hInst = hInstance; // 将实例句柄存储在全局变量中

    HWND hwnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, WIND_WIDTH, WIND_HEIGHT, nullptr, nullptr, hInstance, nullptr);
    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    if (!hwnd)
    {
        return FALSE;
    }
    if (!Game_Init(hwnd)) {
        MessageBox(hwnd, L"ERROR", L"消息窗口", 0);
        return FALSE;
    }
    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MODSDEMO));
    MSG msg = { 0 };

    // 主消息循环:
    while (msg.message != WM_QUIT) {
        if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return (int)msg.wParam;
}
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_MODSDEMO));
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_MODSDEMO);
    wcex.lpszClassName = szWindowClass;
    wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_TIMER:
        Game_Paint(hWnd);
        break;
    case WM_COMMAND:
    {
        int wmId = LOWORD(wParam);
        // 分析菜单选择:
        switch (wmId)
        {
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
    }
    break;
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        // TODO: 在此处添加使用 hdc 的任何绘图代码...
        EndPaint(hWnd, &ps);
    }
    break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}
BOOL Game_Init(HWND hwnd) {
    g_hdc = GetDC(hwnd);
    /*定时播放动画,与过程函数WM_TIMER对应*/
    SetTimer(hwnd, 1, 100, NULL);
    /*开始绘制*/
    Game_Paint(hwnd);
    return true;
}
VOID Game_Paint(HWND hwnd) {
    srand((unsigned)time(NULL));//随机种子
    HBRUSH Brush = CreateSolidBrush(RGB(rand() % 256, rand() % 256, rand() % 256));
    SelectObject(g_hdc, Brush);
    Rectangle(g_hdc, rand() % WIND_WIDTH, rand() % WIND_HEIGHT, rand() % WIND_WIDTH + 50, rand() % WIND_HEIGHT + 50);
    return;
}
VOID Game_Clean(HWND hwnd)
{
    ReleaseDC(hwnd, g_mdc);
    ReleaseDC(hwnd, g_hdc);
    KillTimer(hwnd, 1);
    return;
}

欢迎大家指正

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 在使用Devc进行俄罗斯方块制作前,我们需要先了解一下俄罗斯方块的游戏规则和操作方式。俄罗斯方块是一款经典的益智游戏,玩家需要控制下落方块的运动轨迹,让它们落到正确的位置上以组成一行或多行完整的方块,被消去后就可以得到积分。 制作俄罗斯方块的过程可以分为如下几个步骤: 1. 设计游戏界面,包括游戏背景、方块的样式、方块落下的速度和方向等。 2. 编写游戏逻辑,主要包括方块的运动控制、碰撞检测、消行处理等,需要用到C语言中的一些基本语法和算法知识。 3. 实现用户操作,可以通过键盘输入来控制下落方块的方向和移动速度,使得游戏更加 interactive。 4. 添加音效和动画效果,使得游戏变得更加生动有趣。 将以上步骤整合在一起,我们便可以使用Devc来进行俄罗斯方块制作了。在编写代码时,需要注意逻辑清晰、语法规范,有助于提高程序的可读性和可维护性。同时,需要反复测试,解决程序中可能存在的bug,保证游戏质量和用户体验。 总之,在使用Devc进行俄罗斯方块制作的过程中,需要掌握一定的C语言编程基础,并且注重游戏设计和用户体验,这样才能制作出一款精致、有趣、富有挑战性的俄罗斯方块游戏。 ### 回答2: 俄罗斯方块是一款经典的休闲益智游戏,可在Dev-C++制作。Dev-C++是一款C++编程软件,为了实现俄罗斯方块游戏,我们需要采取以下步骤: 首先,我们需要考虑游戏的逻辑。俄罗斯方块由七种方块积木组成,每种方块由四个小方块组成。游戏开始时,积木块从上方逐渐下落,玩家需要使用方向键控制积木块的移动和旋转,在底部固定后,积木块将形成一行并消除得分。如果积木块达到顶部则游戏结束。 其次,我们需要创建一个游戏界面,包括积木块、得分和游戏状态的显示。使用Dev-C++提供的图形库可以轻松实现这些功能。 接着,我们需要实现程序的基本框架,包括游戏的初始化、主循环和事件处理等。通过编写相应的函数,可以确保游戏逻辑正确运行。 最后,我们需要优化游戏的性能和可玩性,例如增加游戏难度、音效和高分榜等功能。 总之,利用Dev-C++可以轻松制作出一款经典的俄罗斯方块游戏,不仅可以满足编程爱好者的需求,也可以为玩家提供趣味益智的游戏体验。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值