700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 基于C语言设计的俄罗斯方块小游戏(VS运行)

基于C语言设计的俄罗斯方块小游戏(VS运行)

时间:2022-11-03 11:36:42

相关推荐

基于C语言设计的俄罗斯方块小游戏(VS运行)

(1)俄罗斯方块起源

《俄罗斯方块》(Tetris, 俄文:Тетрис)是一款由俄罗斯人阿列克谢·帕基特诺夫于1984年6月发明的休闲游戏。

该游戏曾经被多家公司代理过。经过多轮诉讼后,该游戏的代理权最终被任天堂获得。 任天堂对于俄罗斯方块来说意义重大,因为将它与GB搭配在一起后,获得了巨大的成功。

《俄罗斯方块》的基本规则是移动、旋转和摆放游戏自动输出的各种方块,使之排列成完整的一行或多行并且消除得分。

游戏规则: 由小方块组成的不同形状的板块陆续从屏幕上方落下来,玩家通过调整板块的位置和方向,使它们在屏幕底部拼出完整的一条或几条。这些完整的横条会随即消失,给新落下来的板块腾出空间,与此同时,玩家得到分数奖励。没有被消除掉的方块不断堆积起来,一旦堆到屏幕顶端,玩家便告输,游戏结束。

程序在VS下运行通过测试:

(2)windows下控制台图像控制相关函数分析

因为接下来的俄罗斯方块源码里就需要用到部分函数,要深入理解俄罗斯方块游戏源码,需要先把窗口控制相关的函数搞清楚。

01. CONSOLE_CURSOR_INFO结构

包含有关控制台光标的信息。

typedef struct _CONSOLE_CURSOR_INFO {DWORD dwSize;BOOL bVisible;} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;

dwSize

光标填充的字符单元格的百分比。该值介于1和100之间。光标外观会发生变化,从完全填充单元格到显示为单元格底部的水平线。

注意虽然dwSize值通常介于1和100之间,但在某些情况下,可能会返回该范围之外的值。例如,如果在注册表中将CursorSize设置为0,则返回的dwSize值将为0。

bVisible

光标的可见性。如果光标可见,则此成员为TRUE

02. GetConsoleCursorInfo函数

获取有关指定控制台屏幕缓冲区的光标大小和可见性的信息。

类型声明

BOOL WINAPI GetConsoleCursorInfo(_In_ HANDLEhConsoleOutput,_Out_ PCONSOLE_CURSOR_INFO lpConsoleCursorInfo);功能:获取光标相关信息参数:hConsoleOutput 控制台屏幕缓冲区的句柄。句柄必须具有GENERIC_READ访问权限。lpConsoleCursorInfo 指向CONSOLE_CURSOR_INFO结构的指针,该结构接收有关控制台游标的信息。返回值:如果函数成功,则返回值为非零值。如果函数失败,则返回值为零。要获取扩展错误信息,请调用GetLastError。

03. SetConsoleCursorInfo函数

设置指定控制台屏幕缓冲区的光标大小和可见性。

函数声明:

BOOL WINAPI SetConsoleCursorInfo(_In_ HANDLE hConsoleOutput,_In_ const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo);功能:设置光标的属性参数:hConsoleOutput 控制台屏幕缓冲区的句柄。句柄必须具有GENERIC_READ访问权限。lpConsoleCursorInfo 指向CONSOLE_CURSOR_INFO结构的指针,该结构为控制台屏幕缓冲区的游标提供新规范。返回值:如果函数成功,则返回值为非零值。如果函数失败,则返回值为零。要获取扩展错误信息,请调用GetLastError。

04. SetConsoleCursorPosition函数

设置指定控制台屏幕缓冲区中的光标位置。

函数声明:

BOOL WINAPI SetConsoleCursorPosition(_In_ HANDLE hConsoleOutput,_In_ COORD dwCursorPosition);功能:设置光标的位置参数:hConsoleOutput 控制台屏幕缓冲区的句柄。句柄必须具有GENERIC_READ访问权限。dwCursorPosition 用于指定新的光标位置(以字符为单位)。坐标是屏幕缓冲区字符单元格的列和行。坐标必须位于控制台屏幕缓冲区的边界内。返回值:如果函数成功,则返回值为非零值。如果函数失败,则返回值为零。要获取扩展错误信息,请调用GetLastError。

05. CHAR_INFO结构

指定Unicode或ANSI字符及其属性。控制台功能使用此结构来读取和写入控制台屏幕缓冲区。

类型声明

typedef struct _CHAR_INFO {union {WCHAR UnicodeChar;CHAR AsciiChar;} Char;WORD Attributes;} CHAR_INFO, *PCHAR_INFO;

UnicodeChar

屏幕缓冲区字符单元格的Unicode字符。

AsciiChar

屏幕缓冲区字符单元格的ANSI字符。

Attributes

字符属性。该成员可以是零或以下值的任意组合。

06. ScrollConsoleScreenBuffer函数

在屏幕缓冲区中移动数据块。通过指定剪切矩形可以限制移动的效果,因此剪切矩形外部的控制台屏幕缓冲区的内容不会改变。

函数声明:

BOOL WINAPI ScrollConsoleScreenBuffer(_In_ HANDLEhConsoleOutput,_In_const SMALL_RECT *lpScrollRectangle,_In_opt_ const SMALL_RECT *lpClipRectangle,_In_ COORDdwDestinationOrigin,_In_const CHAR_INFO *lpFill);功能:在屏幕缓冲区中移动数据块参数:hConsoleOutput 控制台屏幕缓冲区的句柄。句柄必须具有GENERIC_READ访问权限。lpScrollRectangle 指定要移动的控制台屏幕缓冲区矩形的左上角和右下角坐标。lpClipRectangle 指定受滚动影响的控制台屏幕缓冲区矩形的左上角和右下角坐标。该指针可以为NULL。dwDestinationOrigin 它以字符为单位指定lpScrollRectangle内容新位置的左上角。lpFill 指向CHAR_INFO结构的指针,该结构指定在填充lpScrollRectangle和lpClipRectangle交集中的单元格时使用的字符和颜色属性,这些属性由于移动而保留为空。返回值:如果函数成功,则返回值为非零值。如果函数失败,则返回值为零。要获取扩展错误信息,请调用GetLastError。

07. FillConsoleOutputAttribute函数

设置指定数量的字符单元格的字符属性,从屏幕缓冲区中的指定坐标开始。

函数声明:

设置指定数量的字符单元格的字符属性BOOL WINAPI FillConsoleOutputAttribute(_In_ HANDLE hConsoleOutput,_In_ WORD wAttribute,_In_ DWORD nLength,_In_ COORD dwWriteCoord,_Out_ LPDWORD lpNumberOfAttrsWritten);功能:设置指定数量的字符单元格的字符属性参数:hConsoleOutput 控制台屏幕缓冲区的句柄。句柄必须具有GENERIC_WRITE访问权限。wAttribute 写入控制台屏幕缓冲区时要使用的属性。nLength 要设置为指定颜色属性的字符单元格数。dwWriteCoord 指定字符坐标的开始位置lpNumberOfAttrsWritten 指向变量的指针,该变量接收实际设置了属性的字符单元格数。返回值:如果函数成功,则返回值为非零值。如果函数失败,则返回值为零。要获取扩展错误信息,请调用GetLastError。

08. FillConsoleOutputCharacter函数

从指定的坐标开始,将一个字符按照指定的次数写入控制台屏幕缓冲区。

函数声明:

BOOL WINAPI FillConsoleOutputCharacter(_In_ HANDLE hConsoleOutput,_In_ TCHAR cCharacter,_In_ DWORD nLength,_In_ COORD dwWriteCoord,_Out_ LPDWORD lpNumberOfCharsWritten);功能:填充指定次数的字符参数:hConsoleOutput 控制台屏幕缓冲区的句柄。句柄必须具有GENERIC_WRITE访问权限。cCharacter 要写入控制台屏幕缓冲区的字符。nLength 应写入字符的字符单元格数。dwWriteCoord 指定字符坐标到的字符是要被写入的开始位置。lpNumberOfCharsWritten 指向变量的指针,该变量接收实际写入控制台屏幕缓冲区的字符数。返回值:如果函数成功,则返回值为非零值。如果函数失败,则返回值为零。要获取扩展错误信息,请调用GetLastError。

09. WriteConsoleOutputCharacter函数

从指定位置开始,将多个字符复制到控制台屏幕缓冲区的连续单元格。

函数声明:

BOOL WINAPI WriteConsoleOutputCharacter(_In_ HANDLE hConsoleOutput,_In_ LPCTSTR lpCharacter,_In_ DWORD nLength,_In_ COORD dwWriteCoord,_Out_ LPDWORD lpNumberOfCharsWritten);功能:从指定位置开始,将多个字符复制到控制台屏幕缓冲区的连续单元格。参数:hConsoleOutput 控制台屏幕缓冲区的句柄。句柄必须具有GENERIC_WRITE访问权限。lpCharacter 要写入控制台屏幕缓冲区的字符串。nLength 要写入的字符数。dwWriteCoord 起始位置lpNumberOfCharsWritten 指向接收实际写入的字符数的变量的指针。返回值:如果函数成功,则返回值为非零值。如果函数失败,则返回值为零。要获取扩展错误信息,请调用GetLastError。

10. GetConsoleTitle函数

获取当前控制台标题

函数声明

DWORD WINAPI GetConsoleTitle(_Out_ LPTSTR lpConsoleTitle,_In_ DWORD nSize);功能:获取当前控制台标题参数:lpConsoleTitle 指向缓冲区的指针,该缓冲区接收包含标题的以null结尾的字符串。如果缓冲区太小而无法存储标题,则该函数将存储符合缓冲区的标题字符,以空终止符结束。nSize 指向的缓冲区大小,以字符为单位。返回值:如果函数成功,则返回值是控制台窗口标题的长度(以字符为单位)。如果函数失败,则返回值为零,GetLastError返回错误代码。

11. SetConsoleTitle函数

设置当前控制台窗口标题

函数声明:

BOOL WINAPI SetConsoleTitle(_In_ LPCTSTR lpConsoleTitle);功能:设置当前控制台窗口标题参数:lpConsoleTitle 要在控制台窗口的标题栏中显示的字符串。总大小必须小于64K。返回值:如果函数成功,则返回值为非零值。如果函数失败,则返回值为零。要获取扩展错误信息,请调用GetLastError。

注意: 当进程终止时,系统将恢复原始控制台标题。

12. SetConsoleScreenBufferSize函数

设置指定控制台屏幕缓冲区的大小。

函数声明:

BOOL WINAPI SetConsoleScreenBufferSize(_In_ HANDLE hConsoleOutput,_In_ COORD dwSize);功能:设置指定控制台屏幕缓冲区的大小。参数:hConsoleOutput 控制台屏幕缓冲区的句柄。句柄必须具有GENERIC_READ访问权限。dwSize 指定控制台屏幕缓冲区的新的大小,在字符行和列。指定的宽度和高度不能小于控制台屏幕缓冲区窗口的宽度和高度。指定的尺寸也不能小于系统允许的最小尺寸。此最小值取决于控制台的当前字体大小(由用户选择)以及GetSystemMetrics函数返回的SM_CXMIN和SM_CYMIN值。返回值:如果函数成功,则返回值为非零值。如果函数失败,则返回值为零。要获取扩展错误信息,请调用GetLastError。

13. SetConsoleWindowInfo函数

设置控制台屏幕缓冲区窗口的当前大小和位置。

函数声明:

BOOL WINAPI SetConsoleWindowInfo(_In_ HANDLEhConsoleOutput,_In_ BOOL bAbsolute,_In_ const SMALL_RECT *lpConsoleWindow);功能:设置控制台屏幕缓冲区窗口的当前大小和位置。参数:hConsoleOutput 控制台屏幕缓冲区的句柄。句柄必须具有GENERIC_READ访问权限。bAbsolute 如果此参数为TRUE,则坐标指定窗口的新左上角和右下角。如果为FALSE,则坐标相对于当前窗口角坐标。lpConsoleWindow 指向SMALL_RECT结构的指针,该结构指定窗口的新左上角和右下角。返回值:如果函数成功,则返回值为非零值。如果函数失败,则返回值为零。要获取扩展错误信息,请调用GetLastError。

14. COORD结构体

定义控制台屏幕缓冲区中字符单元格的坐标。坐标系(0,0)的原点位于缓冲区的左上角。

类型声明

typedef struct _COORD {SHORT X;SHORT Y;} COORD, *PCOORD;

说明:

x: 水平坐标或列值。单位取决于函数调用。

y: 垂直坐标或行值。单位取决于函数调用。

15. SMALL_RECT结构

定义矩形的左上角和右下角的坐标。

类型声明

typedef struct _SMALL_RECT {SHORT Left;SHORT Top;SHORT Right;SHORT Bottom;} SMALL_RECT;

说明:

Left: 矩形左上角的x坐标。

Top: 矩形左上角的y坐标。

Right: 矩形右下角的x坐标。

Bottom: 矩形右下角的y坐标。

16. CONSOLE_SCREEN_BUFFER_INFO结构体

包含有关控制台屏幕缓冲区的信息

类型声明

typedef struct _CONSOLE_SCREEN_BUFFER_INFO {COORDdwSize;COORDdwCursorPosition;WORD wAttributes;SMALL_RECT srWindow;COORDdwMaximumWindowSize;} CONSOLE_SCREEN_BUFFER_INFO;

说明:

dwSize: 包含控制台屏幕缓冲区的大小,以字符列和行为单位。

dwCursorPosition: 包含控制台屏幕缓冲区中光标的列和行坐标。

wAttributes: 字符属性

srWindow: 包含显示窗口左上角和右下角的控制台屏幕缓冲区坐标。

dwMaximumWindowSize: 它包含控制台窗口的最大大小,在字符列和行中,给定当前屏幕缓冲区大小和字体以及屏幕大小。

17. GetConsoleScreenBufferInfo函数

获取有关指定控制台屏幕缓冲区的信息。

函数声明:

BOOL WINAPI GetConsoleScreenBufferInfo(_In_ HANDLE hConsoleOutput,_Out_ PCONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);功能:获取有关指定控制台屏幕缓冲区的信息。参数:hConsoleOutput:控制台屏幕缓冲区的句柄。句柄必须具有GENERIC_READ访问权限。lpConsoleScreenBufferInfo:指向CONSOLE_SCREEN_BUFFER_INFO结构的指针,该结构接收控制台屏幕缓冲区信息。返回值:如果函数成功,则返回值为非零值。如果函数失败,则返回值为零。要获取扩展错误信息,请调用GetLastError。

18. FillConsoleOutputCharacter 函数

从指定的坐标开始,将一个字符写入控制台屏幕缓冲区指定的次数。

函数声明:

BOOL WINAPI FillConsoleOutputCharacter(_In_ HANDLE hConsoleOutput,_In_ TCHAR cCharacter,_In_ DWORD nLength,_In_ COORD dwWriteCoord,_Out_ LPDWORD lpNumberOfCharsWritten);参数:hConsoleOutput 控制台屏幕缓冲区的句柄。句柄必须具有GENERIC_WRITE访问权限。cCharacter 要写入控制台屏幕缓冲区的字符。nLength 应写入字符的字符单元格数。dwWriteCoord 指定字符坐标到的字符是要被写入的开始坐标。lpNumberOfCharsWritten 指向变量的指针,该变量接收实际写入控制台屏幕缓冲区的字符数。返回值:如果函数成功,则返回值为非零值。如果函数失败,则返回值为零。要获取扩展错误信息,请调用GetLastError。

19. 句柄

句柄是Windows最常用的概念。它通常用来标识Windows资源(如菜单、图标、窗口等)和设备等对象。虽然可以把句柄理解为是一个指针变量类型,但它不是对象所在的地址指针,而是作为Windows系统内部表的索引值来使用的。

HANDLE在winnt.h中的声明为

typedef void *HANDLE;

​ 从上面可以看出HANDLE是一种无类型指针,句柄是处理对象的一个接口,你可以通过句柄去操作程序中所涉及的对象。在windows中,句柄是和对象一一对应的32位无符号整数值,对象可以映射到唯一的句柄,

​ 句柄也可以映射到唯一的对象windows需要向程序员提供必要地编程接口,在这些接口中,允许程序员访问,创建和销毁对象,但是,出于封装的考虑,windows并不想向程序员返回指针

​ 如果作数据的话,句柄这种方式则允许你按自己的方式直接操作数据,但windows又不向你直接暴露数据。直接操作数据是程序员需要的,不暴露数据是windows所需要的,句柄封装方式实现了各取所需。

​ “句柄”是Windows最常用的概念。它通常用来标识Windows资源(如菜单、图标、窗口等)和设备等对象。虽然可以把句柄理解为是一个指针变量类型,但它不是对象所在的地址指针,而是作为Windows系统内部表的索引值来使用的。

20. GetStdHandle函数

函数相关说明

HANDLE WINAPI GetStdHandle(_In_ DWORD nStdHandle);功能:获取指定标准设备的句柄(标准输入,标准输出或标准错误)参数:nStdHandle 标准设备。此参数可以是以下值之一。STD_INPUT_HANDLE(DWORD)-10标准输入设备。最初,这是控制台输入缓冲区STD_OUTPUT_HANDLE(DWORD)-11 标准输出设备。最初,这是活动的控制台屏幕缓冲区STD_ERROR_HANDLE(DWORD)-12标准错误设备。最初,这是活动的控制台屏幕缓冲区返回值:如果函数成功,则返回值是指定设备的句柄,或者是先前调用SetStdHandle设置的重定向句柄。该句柄具有GENERIC_READ和GENERIC_WRITE访问权限,除非应用程序使用SetStdHandle设置具有较少访问权限的标准句柄。如果函数失败,则返回值为INVALID_HANDLE_VALUE。要获取扩展错误信息,请调用GetLastError。如果应用程序没有关联的标准句柄,例如在交互式桌面上运行的服务,并且未重定向它们,则返回值为NULL。

在默认情况下:

标准输入(stdin)----键盘

标准输出(stdout)----显示器(屏幕)

标准错误(stderr)----显示器(屏幕)

注意:标准输出句柄和标准错误句柄默认情况下都是对应的屏幕

21. CloseHandle函数

函数相关说明

BOOL CloseHandle(HANDLE hObject);功能:关闭一个打开的对象句柄参数:hObject 打开对象的有效句柄返回值:如果函数成功,则返回值为非零值。如果函数失败,则返回值为零。要获取扩展错误信息,请调用 GetLastError。

22. SetConsoleTextAttribute函数

设置控制台文本属性(颜色),可以设置前景色FOREGROUND(文本颜色)和背景色BACKGROUND

BOOL WINAPI SetConsoleTextAttribute(HANDLE hConsoleOutput, WORD wAttributes);功能:设置控制台文本属性(颜色)参数:hConsoleOutput: 控制台屏幕缓冲区的句柄。句柄必须具有GENERIC_READ访问权限。wAttributes: 字符属性返回值:如果函数成功,则返回值为非零值。如果函数失败,则返回值为零。要获取扩展错误信息,请调用GetLastError。

备注:要确定屏幕缓冲区的当前颜色属性,请调用GetConsoleScreenBufferInfo函数。

字符属性可以分为两类:颜色和DBCS。Wincon.h头文件中定义了以下属性。

相关声明

//// Attributes flags://#define FOREGROUND_BLUE0x0001 // text color contains blue.#define FOREGROUND_GREEN0x0002 // text color contains green.#define FOREGROUND_RED 0x0004 // text color contains red.#define FOREGROUND_INTENSITY 0x0008 // text color is intensified.#define BACKGROUND_BLUE0x0010 // background color contains blue.#define BACKGROUND_GREEN0x0020 // background color contains green.#define BACKGROUND_RED 0x0040 // background color contains red.#define BACKGROUND_INTENSITY 0x0080 // background color is intensified.#define COMMON_LVB_LEADING_BYTE 0x0100 // Leading Byte of DBCS#define COMMON_LVB_TRAILING_BYTE 0x0200 // Trailing Byte of DBCS#define COMMON_LVB_GRID_HORIZONTAL 0x0400 // DBCS: Grid attribute: top horizontal.#define COMMON_LVB_GRID_LVERTICAL 0x0800 // DBCS: Grid attribute: left vertical.#define COMMON_LVB_GRID_RVERTICAL 0x1000 // DBCS: Grid attribute: right vertical.#define COMMON_LVB_REVERSE_VIDEO 0x4000 // DBCS: Reverse fore/back ground attribute.#define COMMON_LVB_UNDERSCORE0x8000 // DBCS: Underscore.#define COMMON_LVB_SBCSDBCS 0x0300 // SBCS or DBCS flag.

23. INPUT_RECORD结构

描述控制台输入缓冲区中的输入事件。可以使用ReadConsoleInputPeekConsoleInput函数从输入缓冲区读取这些记录,也可以使用WriteConsoleInput函数将这些记录写入输入缓冲区。

类型声明:

typedef struct _INPUT_RECORD {WORD EventType;union {KEY_EVENT_RECORDKeyEvent;MOUSE_EVENT_RECORD MouseEvent;WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;MENU_EVENT_RECORD MenuEvent;FOCUS_EVENT_RECORD FocusEvent;} Event;} INPUT_RECORD;

EventType

输入事件类型的句柄和存储在Event成员中的事件记录。

该成员可以是以下值之一。

事件

事件信息。此成员的格式取决于EventType成员指定的事件类型。

24. MOUSE_EVENT_RECORD结构

描述控制台INPUT_RECORD结构中的鼠标输入事件。

类型声明:

typedef struct _MOUSE_EVENT_RECORD {COORD dwMousePosition;DWORD dwButtonState;DWORD dwControlKeyState;DWORD dwEventFlags;} MOUSE_EVENT_RECORD;

成员说明

dwMousePosition

一个COORD结构,它根据控制台屏幕缓冲区的字符单元格坐标包含光标的位置。

dwButtonState

鼠标按钮的状态。最低有效位对应于最左边的鼠标按钮。下一个最低有效位对应于最右边的鼠标按钮。下一位表示从左到右的鼠标按钮。然后,这些位从左到右对应鼠标按钮。如果按下按钮,则位为1。

为前五个鼠标按钮定义了以下常量。

dwControlKeyState

控制键的状态。该成员可以是以下一个或多个值。

dwEventFlags

鼠标事件的类型。如果此值为零,则表示正在按下或释放鼠标按钮。否则,此成员是以下值之一。

25. ReadConsoleInput函数

从控制台输入缓冲区读取数据并将其从缓冲区中删除。

函数声明:

BOOL WINAPI ReadConsoleInput(_In_ HANDLE hConsoleInput,_Out_ PINPUT_RECORD lpBuffer,_In_ DWORD nLength,_Out_ LPDWORD lpNumberOfEventsRead);功能:从控制台输入缓冲区读取数据并将其从缓冲区中删除。参数:hConsoleInput 控制台输入缓冲区的句柄。句柄必须具有GENERIC_READ访问权限。lpBuffer 指向接收输入缓冲区数据的INPUT_RECORD结构数组的指针。nLength 数组元素中lpBuffer参数 指向的数组大小。lpNumberOfEventsRead 指向接收读取的输入记录数的变量的指针。返回值:如果函数成功,则返回值为非零值。如果函数失败,则返回值为零。要获取扩展错误信息,请调用GetLastError。

26. INPUT_RECORD结构

描述控制台输入缓冲区中的输入事件。可以使用ReadConsoleInputPeekConsoleInput函数从输入缓冲区读取这些记录,也可以使用WriteConsoleInput函数将这些记录写入输入缓冲区。

类型声明:

typedef struct _INPUT_RECORD {WORD EventType;union {KEY_EVENT_RECORDKeyEvent;MOUSE_EVENT_RECORD MouseEvent;WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;MENU_EVENT_RECORD MenuEvent;FOCUS_EVENT_RECORD FocusEvent;} Event;} INPUT_RECORD;

EventType

输入事件类型的句柄和存储在Event成员中的事件记录。

该成员可以是以下值之一。

事件

事件信息。此成员的格式取决于EventType成员指定的事件类型。

27. KEY_EVENT_RECORD结构

描述控制台INPUT_RECORD结构中的键盘输入事件。

typedef struct _KEY_EVENT_RECORD {BOOL bKeyDown;WORD wRepeatCount;WORD wVirtualKeyCode;WORD wVirtualScanCode;union {WCHAR UnicodeChar;CHAR AsciiChar;} uChar;DWORD dwControlKeyState;} KEY_EVENT_RECORD;

bKeyDown

如果按下该键,则该成员为TRUE。否则,此成员为FALSE(密钥已释放)。

wRepeatCount

重复计数,表示正在按住某个键。例如,当按下某个键时,您可能会获得五个事件,该成员等于1,一个事件的成员等于5,或者此成员的多个事件大于或等于1。

wVirtualKeyCode

虚拟键码识别在设备无关的方式给定的键。

wVirtualScanCode

给定键的虚拟扫描代码,表示键盘硬件生成的与设备相关的值。

uChar

以下成员的联盟。

UnicodeChar

翻译的Unicode字符。

AsciiChar

翻译的ASCII字符。

dwControlKeyState

控制键的状态。该成员可以是以下一个或多个值。

虚拟键值码表

下表显示了系统使用的虚拟键代码的符号常量名称,十六进制值以及鼠标或键盘等效项。代码按数字顺序列出。

官方参考手册:/en-us/windows/desktop/inputdev/virtual-key-codes

28. ReadConsoleInput函数

从控制台输入缓冲区读取数据并将其从缓冲区中删除。

函数声明:

BOOL WINAPI ReadConsoleInput(_In_ HANDLE hConsoleInput,_Out_ PINPUT_RECORD lpBuffer,_In_ DWORD nLength,_Out_ LPDWORD lpNumberOfEventsRead);功能:从控制台输入缓冲区读取数据并将其从缓冲区中删除。参数:hConsoleInput 控制台输入缓冲区的句柄。句柄必须具有GENERIC_READ访问权限。lpBuffer 指向接收输入缓冲区数据的INPUT_RECORD结构数组的指针。nLength 数组元素中lpBuffer参数 指向的数组大小。lpNumberOfEventsRead 指向接收读取的输入记录数的变量的指针。返回值:如果函数成功,则返回值为非零值。如果函数失败,则返回值为零。要获取扩展错误信息,请调用GetLastError。

官方参考网址:/en-us/windows/console/readconsoleinput

(3)俄罗斯方块源码

#include <stdio.h>#include <Windows.h>#include <stdlib.h>#include <time.h>#include <conio.h>#define ROW 29 //游戏区行数#define COL 20 //游戏区列数#define DOWN 80 //方向键:下#define LEFT 75 //方向键:左#define RIGHT 77 //方向键:右#define SPACE 32 //空格键#define ESC 27 //Esc键struct Face{int data[ROW][COL + 10]; //用于标记指定位置是否有方块(1为有,0为无)int color[ROW][COL + 10]; //用于记录指定位置的方块颜色编码}face;struct Block{int space[4][4];}block[7][4]; //用于存储7种基本形状方块的各自的4种形态的信息,共28种//隐藏光标void HideCursor();//光标跳转void CursorJump(int x, int y);//初始化界面void InitInterface();//初始化方块信息void InitBlockInfo();//颜色设置void color(int num);//画出方块void DrawBlock(int shape, int form, int x, int y);//空格覆盖void DrawSpace(int shape, int form, int x, int y);//合法性判断int IsLegal(int shape, int form, int x, int y);//判断得分与结束int JudeFunc();//游戏主体逻辑函数void StartGame();//从文件读取最高分void ReadGrade();//更新最高分到文件void WriteGrade();int max, grade; //全局变量int main(){#pragma warning (disable:4996) //消除警告max = 0, grade = 0; //初始化变量system("title 经典游戏-俄罗斯方块(VS运行)"); //设置cmd窗口的名字system("mode con lines=29 cols=60"); //设置cmd窗口的大小HideCursor(); //隐藏光标ReadGrade(); //从文件读取最高分到max变量InitInterface(); //初始化界面InitBlockInfo(); //初始化方块信息srand((unsigned int)time(NULL)); //设置随机数生成的起点StartGame(); //开始游戏return 0;}//隐藏光标void HideCursor(){CONSOLE_CURSOR_INFO curInfo; //定义光标信息的结构体变量curInfo.dwSize = 1; //如果没赋值的话,隐藏光标无效curInfo.bVisible = FALSE; //将光标设置为不可见HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台句柄SetConsoleCursorInfo(handle, &curInfo); //设置光标信息}//光标跳转void CursorJump(int x, int y){COORD pos; //定义光标位置的结构体变量pos.X = x; //横坐标设置pos.Y = y; //纵坐标设置HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台句柄SetConsoleCursorPosition(handle, pos); //设置光标位置}//初始化界面void InitInterface(){color(7); //颜色设置为白色for (int i = 0; i < ROW; i++){for (int j = 0; j < COL + 10; j++){if (j == 0 || j == COL - 1 || j == COL + 9){face.data[i][j] = 1; //标记该位置有方块CursorJump(2 * j, i);printf("■");}else if (i == ROW - 1){face.data[i][j] = 1; //标记该位置有方块printf("■");}elseface.data[i][j] = 0; //标记该位置无方块}}for (int i = COL; i < COL + 10; i++){face.data[8][i] = 1; //标记该位置有方块CursorJump(2 * i, 8);printf("■");}CursorJump(2 * COL, 1);printf("下一个方块:");CursorJump(2 * COL + 4, ROW - 19);printf("左移:←");CursorJump(2 * COL + 4, ROW - 17);printf("右移:→");CursorJump(2 * COL + 4, ROW - 15);printf("加速:↓");CursorJump(2 * COL + 4, ROW - 13);printf("旋转:空格");CursorJump(2 * COL + 4, ROW - 11);printf("暂停: S");CursorJump(2 * COL + 4, ROW - 9);printf("退出: Esc");CursorJump(2 * COL + 4, ROW - 7);printf("重新开始:R");CursorJump(2 * COL + 4, ROW - 5);printf("最高纪录:%d", max);CursorJump(2 * COL + 4, ROW - 3);printf("当前分数:%d", grade);}//初始化方块信息void InitBlockInfo(){//“T”形for (int i = 0; i <= 2; i++)block[0][0].space[1][i] = 1;block[0][0].space[2][1] = 1;//“L”形for (int i = 1; i <= 3; i++)block[1][0].space[i][1] = 1;block[1][0].space[3][2] = 1;//“J”形for (int i = 1; i <= 3; i++)block[2][0].space[i][2] = 1;block[2][0].space[3][1] = 1;for (int i = 0; i <= 1; i++){//“Z”形block[3][0].space[1][i] = 1;block[3][0].space[2][i + 1] = 1;//“S”形block[4][0].space[1][i + 1] = 1;block[4][0].space[2][i] = 1;//“O”形block[5][0].space[1][i + 1] = 1;block[5][0].space[2][i + 1] = 1;}//“I”形for (int i = 0; i <= 3; i++)block[6][0].space[i][1] = 1;int temp[4][4];for (int shape = 0; shape < 7; shape++) //7种形状{for (int form = 0; form < 3; form++) //4种形态(已经有了一种,这里每个还需增加3种){//获取第form种形态for (int i = 0; i < 4; i++){for (int j = 0; j < 4; j++){temp[i][j] = block[shape][form].space[i][j];}}//将第form种形态顺时针旋转,得到第form+1种形态for (int i = 0; i < 4; i++){for (int j = 0; j < 4; j++){block[shape][form + 1].space[i][j] = temp[3 - j][i];}}}}}//颜色设置void color(int c){switch (c){case 0:c = 13; //“T”形方块设置为紫色break;case 1:case 2:c = 12; //“L”形和“J”形方块设置为红色break;case 3:case 4:c = 10; //“Z”形和“S”形方块设置为绿色break;case 5:c = 14; //“O”形方块设置为黄色break;case 6:c = 11; //“I”形方块设置为浅蓝色break;default:c = 7; //其他默认设置为白色break;}SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //颜色设置//注:SetConsoleTextAttribute是一个API(应用程序编程接口)}//画出方块void DrawBlock(int shape, int form, int x, int y){for (int i = 0; i < 4; i++){for (int j = 0; j < 4; j++){if (block[shape][form].space[i][j] == 1) //如果该位置有方块{CursorJump(2 * (x + j), y + i); //光标跳转到指定位置printf("■"); //输出方块}}}}//空格覆盖void DrawSpace(int shape, int form, int x, int y){for (int i = 0; i < 4; i++){for (int j = 0; j < 4; j++){if (block[shape][form].space[i][j] == 1) //如果该位置有方块{CursorJump(2 * (x + j), y + i); //光标跳转到指定位置printf(" "); //打印空格覆盖(两个空格)}}}}//合法性判断int IsLegal(int shape, int form, int x, int y){for (int i = 0; i < 4; i++){for (int j = 0; j < 4; j++){//如果方块落下的位置本来就已经有方块了,则不合法if ((block[shape][form].space[i][j] == 1) && (face.data[y + i][x + j] == 1))return 0; //不合法}}return 1; //合法}//判断得分与结束int JudeFunc(){//判断是否得分for (int i = ROW - 2; i > 4; i--){int sum = 0; //记录第i行的方块个数for (int j = 1; j < COL - 1; j++){sum += face.data[i][j]; //统计第i行的方块个数}if (sum == 0) //该行没有方块,无需再判断其上的层次(无需再继续判断是否得分)break; //跳出循环if (sum == COL - 2) //该行全是方块,可得分{grade += 10; //满一行加10分color(7); //颜色设置为白色CursorJump(2 * COL + 4, ROW - 3); //光标跳转到显示当前分数的位置printf("当前分数:%d", grade); //更新当前分数for (int j = 1; j < COL - 1; j++) //清除得分行的方块信息{face.data[i][j] = 0; //该位置得分后被清除,标记为无方块CursorJump(2 * j, i); //光标跳转到该位置printf(" "); //打印空格覆盖(两个空格)}//把被清除行上面的行整体向下挪一格for (int m = i; m > 1; m--){sum = 0; //记录上一行的方块个数for (int n = 1; n < COL - 1; n++){sum += face.data[m - 1][n]; //统计上一行的方块个数face.data[m][n] = face.data[m - 1][n]; //将上一行方块的标识移到下一行face.color[m][n] = face.color[m - 1][n]; //将上一行方块的颜色编号移到下一行if (face.data[m][n] == 1) //上一行移下来的是方块,打印方块{CursorJump(2 * n, m); //光标跳转到该位置color(face.color[m][n]); //颜色设置为还方块的颜色printf("■"); //打印方块}else //上一行移下来的是空格,打印空格{CursorJump(2 * n, m); //光标跳转到该位置printf(" "); //打印空格(两个空格)}}if (sum == 0) //上一行移下来的全是空格,无需再将上层的方块向下移动(移动结束)return 1; //返回1,表示还需调用该函数进行判断(移动下来的可能还有满行)}}}//判断游戏是否结束for (int j = 1; j < COL - 1; j++){if (face.data[1][j] == 1) //顶层有方块存在(以第1行为顶层,不是第0行){Sleep(1000); //留给玩家反应时间system("cls"); //清空屏幕color(7); //颜色设置为白色CursorJump(2 * (COL / 3), ROW / 2 - 3);if (grade > max){printf("恭喜你打破最高记录,最高记录更新为%d", grade);WriteGrade();}else if (grade == max){printf("与最高记录持平,加油再创佳绩", grade);}else{printf("请继续加油,当前与最高记录相差%d", max - grade);}CursorJump(2 * (COL / 3), ROW / 2);printf("GAME OVER");while (1){char ch;CursorJump(2 * (COL / 3), ROW / 2 + 3);printf("再来一局?(y/n):");scanf("%c", &ch);if (ch == 'y' || ch == 'Y'){system("cls");main();}else if (ch == 'n' || ch == 'N'){CursorJump(2 * (COL / 3), ROW / 2 + 5);exit(0);}else{CursorJump(2 * (COL / 3), ROW / 2 + 4);printf("选择错误,请再次选择");}}}}return 0; //判断结束,无需再调用该函数进行判断}//游戏主体逻辑函数void StartGame(){int shape = rand() % 7, form = rand() % 4; //随机获取方块的形状和形态while (1){int t = 0;int nextShape = rand() % 7, nextForm = rand() % 4; //随机获取下一个方块的形状和形态int x = COL / 2 - 2, y = 0; //方块初始下落位置的横纵坐标color(nextShape); //颜色设置为下一个方块的颜色DrawBlock(nextShape, nextForm, COL + 3, 3); //将下一个方块显示在右上角while (1){color(shape); //颜色设置为当前正在下落的方块DrawBlock(shape, form, x, y); //将该方块显示在初始下落位置if (t == 0){t = 15000; //这里t越小,方块下落越快(可以根据此设置游戏难度)}while (--t){if (kbhit() != 0) //若键盘被敲击,则退出循环break;}if (t == 0) //键盘未被敲击{if (IsLegal(shape, form, x, y + 1) == 0) //方块再下落就不合法了(已经到达底部){//将当前方块的信息录入face当中//face:记录界面的每个位置是否有方块,若有方块还需记录该位置方块的颜色。for (int i = 0; i < 4; i++){for (int j = 0; j < 4; j++){if (block[shape][form].space[i][j] == 1){face.data[y + i][x + j] = 1; //将该位置标记为有方块face.color[y + i][x + j] = shape; //记录该方块的颜色数值}}}while (JudeFunc()); //判断此次方块下落是否得分以及游戏是否结束break; //跳出当前死循环,准备进行下一个方块的下落}else //未到底部{DrawSpace(shape, form, x, y); //用空格覆盖当前方块所在位置y++; //纵坐标自增(下一次显示方块时就相当于下落了一格了)}}else //键盘被敲击{char ch = getch(); //读取keycodeswitch (ch){case DOWN: //方向键:下if (IsLegal(shape, form, x, y + 1) == 1) //判断方块向下移动一位后是否合法{//方块下落后合法才进行以下操作DrawSpace(shape, form, x, y); //用空格覆盖当前方块所在位置y++; //纵坐标自增(下一次显示方块时就相当于下落了一格了)}break;case LEFT: //方向键:左if (IsLegal(shape, form, x - 1, y) == 1) //判断方块向左移动一位后是否合法{//方块左移后合法才进行以下操作DrawSpace(shape, form, x, y); //用空格覆盖当前方块所在位置x--; //横坐标自减(下一次显示方块时就相当于左移了一格了)}break;case RIGHT: //方向键:右if (IsLegal(shape, form, x + 1, y) == 1) //判断方块向右移动一位后是否合法{//方块右移后合法才进行以下操作DrawSpace(shape, form, x, y); //用空格覆盖当前方块所在位置x++; //横坐标自增(下一次显示方块时就相当于右移了一格了)}break;case SPACE: //空格键if (IsLegal(shape, (form + 1) % 4, x, y + 1) == 1) //判断方块旋转后是否合法{//方块旋转后合法才进行以下操作DrawSpace(shape, form, x, y); //用空格覆盖当前方块所在位置y++; //纵坐标自增(总不能原地旋转吧)form = (form + 1) % 4; //方块的形态自增(下一次显示方块时就相当于旋转了)}break;case ESC: //Esc键system("cls"); //清空屏幕color(7);CursorJump(COL, ROW / 2);printf(" 游戏结束 ");CursorJump(COL, ROW / 2 + 2);exit(0); //结束程序case 's':case 'S': //暂停system("pause>nul"); //暂停(按任意键继续)break;case 'r':case 'R': //重新开始system("cls"); //清空屏幕main(); //重新执行主函数}}}shape = nextShape, form = nextForm; //获取下一个方块的信息DrawSpace(nextShape, nextForm, COL + 3, 3); //将右上角的方块信息用空格覆盖}}//从文件读取最高分void ReadGrade(){FILE* pf = fopen("俄罗斯方块最高得分记录.txt", "r"); //以只读方式打开文件if (pf == NULL) //打开文件失败{pf = fopen("俄罗斯方块最高得分记录.txt", "w"); //以只写方式打开文件(文件不存在可以自动创建该文件)fwrite(&grade, sizeof(int), 1, pf); //将max写入文件(此时max为0),即将最高历史得分初始化为0}fseek(pf, 0, SEEK_SET); //使文件指针pf指向文件开头fread(&max, sizeof(int), 1, pf); //读取文件中的最高历史得分到max当中fclose(pf); //关闭文件pf = NULL; //文件指针及时置空}//更新最高分到文件void WriteGrade(){FILE* pf = fopen("俄罗斯方块最高得分记录.txt", "w"); //以只写方式打开文件if (pf == NULL) //打开文件失败{printf("保存最高得分记录失败\n");exit(0);}fwrite(&grade, sizeof(int), 1, pf); //将本局游戏得分写入文件当中(更新最高历史得分)fclose(pf); //关闭文件pf = NULL; //文件指针及时置空}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。