c语言贪食蛇游戏

演示视频

目录

一.概述 

二.游戏开始前

修改控制台程序标题和大小

 Win32 API

 GetStdHandle函数

GetConsoleCursorInfo函数和SetConsoleCursorInfo函数

SetConsoleCursorPosition函数 

游戏开篇界面处理

创建地图

蛇身节点以及食物节点初始化

蛇身的初始化 

整体蛇节点初始化的代码

食物节点初始化

食物节点初始化完整代码 

三.游戏运行阶段 

游戏按键的设置

虚拟按键代码

snakemove移动函数

撞墙机制

咬到自己机制 

游戏运行阶段的全部代码如下,从下往上看

五.贪食蛇完整代码

测试.c文件代码

贪食蛇的声明.h文件

贪食蛇的实现.h代码


一.概述 

贪食蛇游戏设计,分为游戏开始前和游戏运行以及游戏结束三个阶段,我这个是利用win32 API直接在命令框设计的游戏,游戏运行阶段主要是解决游戏界面,提示信息等方面。游戏运行阶段会去解决初始化蛇身和食物,以及根据按键情况去移动蛇的方面。游戏结束阶段会告知游戏结束的原因和释放链表节点(蛇身以及食物都是通过链表来表示,其实也可以通过动态顺序表来做),游戏结束一般来说会是撞墙结束,咬到自己结束,以及正常退出三种情况。

二.游戏开始前

正常的控制台程序结束标题位置一般都是默认给出了,如果要修改标题要怎么修改呢,控制台程序命令框的大小能不能修改呢。同时光标一闪一闪很影响观感,也应该隐藏起来。

修改控制台程序标题和大小

对于windows命令框可以直接通过  title 新名称来修改命令框标题

 而命令框的大小可以通过mode con cols=要修改的大小 lines=有修改的大小,来进行修改大小,cols是行大小,lines是列大小

而对于编译器来说可不可以使用同样的语句来修改控制台程序界面的大小呢

 还没使用就已经报错了,如果你想使用和windows系统一样的语句进行修改,那么需要加上windows.h头文件,并且使用system才能使用系统语句

代码和运行结果如下,使用getchar()是使程序一直停留在运行阶段,方便测试,如果不这样的话运行结束会直接还原

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<windows.h>
void text()
{system("title 贪食蛇");system("mode con cols=100 lines=30");getchar();
}
int main()
{text();
}

 2.光标的隐藏以及改变输入位

 Win32 API

控制程序光标的各种操作是通过win32 API来进行操作的,Windows这个多作业系统除了协调应⽤程序的执行、分配内存、管理资源之外,它同时也是⼀个很大的服务中心,调⽤这个服务中心的各种服务(每⼀种服务就是⼀个函数),可以帮应用程序达到开启视窗、描绘图形、使用周边设备等目的,由于这些函数服务的对象是应⽤程序(Application),所以便称之为Application Programming Interface,简称API函数。WIN32 API也就是Microsoft Windows 32位平台的应⽤程序编程接⼝。

不过话说回来,如果你要画图又没怎么接触这些图形库,还是先用Easyx吧,这个更容易初学者上手一点,Win32要处理很多细节

Win32 API是windows系统提供的,所以直接使用windows.h头文件就可以使用了

 GetStdHandle函数

 GetStdHandle是用来获取句柄的函数,属于windows API函数,GetStdHandle是⼀个Windows API函数。它⽤于从⼀个特定的标准设备(标准输⼊、标准输出或标准错误)中取得⼀个句柄(⽤来标识不同设备的数值),使⽤这个句柄可以操作设备。句柄说直白点其实就是一个“授权的凭证”,你要通过这个“授权许可凭证”才能进行一系列操作,可以操纵鼠标的光标,还可以用来控制窗口的位置、大小和状态。

语法如下

HANDLE  GetStdHandle(DWORD nStdHandle
);

函数参数如下 

在Windows编程中,标准输入、标准输出和标准错误的句柄值分别为-10、-11和-12。这些特殊的负数值是为了与普通句柄值区分开来。通常情况下,普通句柄值是正整数,而这些特殊句柄值是为了表示标准输入输出而特意赋予的负数值。其实你不填参数值,它也默认是-10,-11,-12

比如获取标准输出的句柄可以表示为HANDLE WINAPI GetStdHandle( STD_OUTPUT_HANDLE)

HANDLE是一种数据类型,是专门接收句柄用的。

GetConsoleCursorInfo函数和SetConsoleCursorInfo函数

GetConsoleCursorInfo函数是用来查看检索光标大小和可见性信息的函数,SetConsoleCursorInfo是将修改后的结果设置回去的函数。

具体用法是首先获取句柄,然后通过GetConsoleCursorInfo函数来获取光标信息,再然后进行修改,然后通过SetConsoleCursorInfo把修改后的结果设置回去

HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);//获取句柄CONSOLE_CURSOR_INFO Info;GetConsoleCursorInfo(houtput, &Info);//获取光标控制台信息Info.bVisible = false;//隐藏控制台光标SetConsoleCursorInfo(houtput, &Info);//把修改后的结果设置回去

  CONSOLE_CURSOR_INFO这是个结构体,是专门用来存放光标信息的结构体,这个结构体成员是是光标可见性和光标大小

bVisible 是光标可见性,false是隐藏,true是正常显示。有些c编译器不支持布尔值 ,用0表示false,1表示true也可以实现操作

dwSize是光标大小,现在的光标大小一般默认是百分之25,介于1到100之间

SetConsoleCursorPosition函数 

这个函数是用来设置光标位置的,一般光标是默认放在左上角进行输出的,而想到屏幕中间输出文字可以通过这个函数来实现。

COORD是存放光标位置的结构体,成员是横坐标x,纵坐标y

COORD定义

用法如下

void setpos(int x, int y)
{COORD pos = { x,y };HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);SetConsoleCursorPosition(houtput, pos);
}

因为要用到很多次,所以单独设置一个函数

游戏开篇界面处理

然后就可以开始准备游戏界面的处理了

首先打印开篇界面

 此时开篇界面的代码

void setpos(int x, int y)
{COORD pos = { x,y };HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);SetConsoleCursorPosition(houtput, pos);
}

那么如何控制请按任意键继续呢,这些字不是直接打印出来的,windows提供了系统函数pause进行暂停,请按任意键继续是这个函数调用了自动产生的

void welcome()
{setpos(40, 10);printf("欢迎来到贪食蛇小游戏\n");setpos(43, 15);system("pause");getchar();
}

然后开始处理第二个界面 

但是在打印之前要先清理上一个界面的数据,通过windows自带的cls就可以清屏了

void welcomemap()
{setpos(40, 10);printf("欢迎来到贪食蛇游戏\n");setpos(41, 15);system("pause");system("cls");//清屏setpos(38, 10);printf("用 ↑ ↓ → ←键操作蛇\n");setpos(38, 13);printf("f3表示加速,f4表示减速,加速能得到更多的分数");setpos(38, 18);system("pause");system("cls");
}//欢迎界面

处理完就这个样子了

 

创建地图

地图的打印我采用的是宽字符“□”,单字符(一个字节的空间)通常是指一个字母、数字或标点符号,但是复杂的特殊符号方框五角星之类的用的是宽字符(两个字节的空间),c语言是美国人发明的,默认ASCII编码形式,所以要打印宽字符需要setlocale函数来进行本地化,具体用法如下

setlocale(LC_ALL, "");

 头文件是<locale.h>,LC_ALL是针对所有项进行修改,具体有
• 数字量的格式 LC_NUMERIC
• 货币量的格式    LC_MONETARY      • 字符集  LC_CTYPE
• ⽇期和时间的表⽰形式 LC_TIME      •所有格式LC_ALL

""双引号表示的是默认本地化,vs编译器双引号里面不加空格,否则会本地化失败,也不会报错,反正就一直打印不出来宽字符。也可以写的具体点setlocale(LC_ALL, "zh_CN");或者指定编码格式setlocale(LC_ALL, "zh_CN.UTF-8");
setlocale(LC_ALL, NULL);是不进行任何操作,仅仅用来查看当前locale设置成了什么

设置完之后就可以着手打印地图了,需要注意的是宽字符只是横坐标x占两个字节,但是纵坐标依旧是一个字节,大致如下

所以一个方格横坐标相当于纵坐标的两倍,如果我想把墙体设置为正方形的话,横坐标是0到54,坐标最后到了56除二为28个格子,而纵坐标是1到27共27个格子,因为横坐标已经打印了0,所以纵坐标从1开始,加上0坐标的各种也28个格子

#define wall L'□'

 

宽字符打印格式是wprintf,我已经提前 把要作为墙体的宽字符方框□要宏定义了,方便修改#define wall L'□'

因为宽字符是占两个字节,所以它每次打印的横坐标x都必须是2的倍数(0也是2的倍数),所以每次i都要+2而不是往常的i++,而第一个横着的墙体纵坐标y是不变的,一直是0,只需要改变x就行

接下来打印最左边竖着的墙,x轴是不变的,y轴每次都要变,所以只需要改变y值就行了,y轴还是一个字节每次加1就行,第0行横坐标的时候已经打印了,所以从1开始打印

然后是最下面横着的墙打印,y轴是一直保持27不变的,竖着的墙坐标到27就停止了,所以跟它连接的横着的墙y轴从27开始。x轴因为要和竖着的行衔接,所以第0行其实已经被打印过了,所以从2开始,每次增加2(宽字符x轴占两个字节)

再然后最右边竖着的墙 ,x轴不变,打印最上面墙的时候条件是i<56,这里没有等号,所以到不了56,而每次i都加2,所以最右边竖着的墙从54开始往下打印就行。也就是x一直保持54,而y轴变化

,由于第一行和最后一行都已经打印过了,所以条件为int i = 1; i < 27; i++

再然后打印右侧的提示信息,这个自己设置好光标直接打印就行,最右的墙最后坐标是56,所以x轴坐标设置要大于56

蛇身节点以及食物节点初始化

我采用的是单链表作为蛇身节点和食物节点用的,所以我采用了结构体snakenode首先对蛇身节点和食物节点都进行初始化,其次贪食蛇整体不只有蛇身节点这一个属性,还有方向 游戏状态,食物分数,总分等多个属性,所以又用另外一个结构体snakegame来表示游戏的各种属性

snakenode节点只有两个成员横坐标x,纵坐标y

 snakegame是维护整条贪食蛇的

因为游戏状态和方向比较多,所以用枚举方式一一表示了

蛇身的初始化 

蛇身的初始化采用的是不带头结点的单链表的头插法,在调用初始化函数snakeInit之前就已经把头结点置为空了,然后循环建立节点cur,如果蛇链表的头结点为空,那么就让第一个cur作为头结点

if (ps->psnake == NULL)
{cur->next = ps->psnake;//最后一个节点置空ps->psnake = cur;
}

因为头插法第一个插入的节点是链表的最后一个,是需要置空的,而ps->psnake之前初始化的时已经置为空了,所以cur->next=ps->psnake; 

如果头节点不为空,那么就将新建节点的next指向头结点,然后将ps->snake头节点指向新建立的节点,这样新建立的节点成了新的头节点

else
{cur->next = ps->psnake;ps->psnake = cur;
}

蛇身节点我是准备设置五个节点,因为每个宽字符占两个字节,所以i+=2,把cur的x坐标和y坐标设置,y的坐标设置为5不动,只改变x的坐标。cur的坐标设置完了之后用setpos函数在同样的坐标上打印身体宽字符图案

for循环的条件如下

for (int i = 0; i <10; i+=2)

cur坐标和打印身体如下

cur->x = 24 + i;
cur->y = 5;//设置cur的x和y坐标
setpos(cur->x, cur->y);//在cur坐标上打印蛇的身体
wprintf(L"%lc", BODY);

再然后顺便初始化一下贪食蛇的其他数据

ps->Dir = right;//方向
ps->foodweight = 10;//一个食物的分数
ps->score = 0;//总分
ps->sleeptime = 200;//休眠时间
ps->statues = ok;//游戏状态

整体蛇节点初始化的代码
void snakeInit(Snake* ps)
{for (int i = 0; i <10; i+=2){snakenode* cur = (snakenode*)malloc(sizeof(snakenode));if (ps->psnake == NULL){cur->next = ps->psnake;ps->psnake = cur;}else{cur->next = ps->psnake;ps->psnake = cur;}cur->x = 24 + i;cur->y = 5;//设置cur的x和y坐标setpos(cur->x, cur->y);//在cur坐标上打印蛇的身体wprintf(L"%lc", BODY);}ps->Dir = right;//方向ps->foodweight = 10;//一个食物的分数ps->score = 0;//总分ps->sleeptime = 200;//休眠时间ps->statues = ok;//游戏状态
}//蛇身节点初始化

食物节点初始化

食物的位置是随机出现的,食物节点的初始化要用到随机函数生成随机数,请注意这个随机是针对节点的成员x和y来说的。创建随机x和y轴时要注意不能生成的节点在蛇身体的五个节点上,然后要在墙里面不能在墙上或者墙外面。其次y没什么要求,在墙内就行,而x必须是2的倍数,因为节点都采用了宽字符打印,如果不是2的倍数,有可能生成的食物半边在墙内,另外半边在墙外

	int x; int y;
again:do {x = rand()%51 + 2;y = rand()%26 + 1;//控制节点坐标生成在墙内} while (x % 2 != 0);//宽字符x必须是2的倍数snakenode* cur = ps->psnake;while (cur){if (x == cur->x &&y == cur->y)//判断生成的坐标是否是蛇身节点goto again;//如果是蛇身节点那么就跳转回上面重新生成x,ycur = cur->next;}

对于rand函数,rand()%51是生成0到50之间的随机数,加2 就变成了生成2到52之间的随机数(包括52),因为如果坐标是0,那么就生成在竖着的墙上了,坐标是54就生成在最右边竖着的墙上了。所以范围是2到52。rand()%26 + 1也是同理

食物节点初始化完整代码 
void foodInit(Snake* ps)
{int x; int y;
again:do {x = rand()%51 + 2;y = rand()%26 + 1;//控制节点坐标生成在墙内} while (x % 2 != 0);//宽字符x必须是2的倍数snakenode* cur = ps->psnake;while (cur){if (x == cur->x &&y == cur->y)//判断生成的坐标是否是蛇身节点goto again;//如果是蛇身节点那么就跳转回上面重新生成x,ycur = cur->next;}ps->pfood = (snakenode*)malloc(sizeof(snakenode));//pfood是维护食物节点的指针ps->pfood->x = x; ps->pfood->y = y;setpos(ps->pfood->x, ps->pfood->y);wprintf(L"%lc", FOOD);
}

三.游戏运行阶段 

这个阶段回去处理游戏按键与节点怎么对应起来以及游戏是怎么运行的

游戏按键的设置

如何将按键与游戏操作结合起来呢,将键盘上每个键的虚拟键值传递给函数,函数通过返回值来分辨按键的状态。win32 API给了一个函数GetAsyncKeyState ,GetAsyncKeyState 的返回值是short类型,在上⼀次调⽤ GetAsyncKeyState 函数后,如果返回的16位的short数据中,最⾼位是1,说明按键的状态是按下,如果最⾼是0,说明按键的状态是抬起;如果最低位被置为1则说明,该按键被按过,否则为0。

贪食蛇游戏检查最低位是不是1就可以了,可以用GetAsyncKeyState的返回值按位&1就可以知道最低是1还是0了

GetAsyncKeyState函数有一个参数,即虚拟键码(Virtual Key Code),用于指定要检查状态按键。

贪食蛇游戏我们只需要用到上下左右,F3 F4 空格,esc退出就可以了

在代码开头写成宏方便操作#define KEY_PRESS(VK) ((GetAsyncKeyState(VK)&0x1) ? 1 : 0)

这个VK就是我们要检查的具体虚拟按键代码,比如要检查↑,那么就是 KEY_PRESS(VK_UP),具体虚拟按键表格如下

虚拟按键代码
虚拟按键描述
VK_LBUTTON0x01鼠标左键
VK_RBUTTON0x02鼠标右键
VK_CANCEL0x03控制中断处理
VK_MBUTTON0x04鼠标中键
VK_XBUTTON10x05X1 鼠标按钮
VK_XBUTTON20x06X2 鼠标按钮
-0x07保留
VK_BACK0x08BACKSPACE 键
VK_TAB0x09TAB 键
-0x0A-0B保留
VK_CLEAR0x0CCLEAR 键
VK_RETURN0x0DENTER键
-0x0E-0楼未分配
VK_SHIFT0x10换档键
VK_CONTROL0x11CTRL 键
VK_MENU0x12Alt 键
VK_PAUSE0x13暂停键
VK_CAPITAL0x14CAPS LOCK 键
VK_KANA0x15IME 假名模式
VK_HANGUL0x15IME 韩文模式
VK_IME_ON0x16IME 开启
VK_JUNJA0x17IME Junja 模式
VK_FINAL0x18IME 最终模式
VK_HANJA0x19IME 汉字模式
VK_KANJI0x19IME 汉字模式
VK_IME_OFF0x1AIME 关闭
VK_ESCAPE0x1B电调键
VK_CONVERT0x1CIME 转换
VK_NONCONVERT0x1DIME 非转换
VK_ACCEPT0x1EIME 接受
VK_MODECHANGE0x1FIME 模式更改请求
VK_SPACE0x20空格键
VK_PRIOR0x21PAGE UP 键
VK_NEXT0x22PAGE DOWN 键
VK_END0x23END 键
VK_HOME0x24HOME键
VK_LEFT0x25向左箭头键
VK_UP0x26向上箭头键
VK_RIGHT0x27向右箭头键
VK_DOWN0x28向下箭头键
VK_SELECT0x29SELECT 键
VK_PRINT0x2APRINT 密钥
VK_EXECUTE0x2BEXECUTE 键
VK_SNAPSHOT0x2CPRINT SCREEN 键
VK_INSERT0x2DINS 密钥
VK_DELETE0x2EDEL键
VK_HELP0x2FHELP 键
0x300 键
0x311 键
0x322 键
0x333键
0x344键
0x355键
0x366键
0x377键
0x388键
0x399键
-0x3A-40定义
0x41一把钥匙
0x42B键
0x43C键
0x44D键
0x45E键
0x46F键
0x47G键
0x48H键
0x49I 键
0x4AJ 键
0x4BK键
0x4CL键
0x4DM键
0x4EN键
0x4FO键
0x50P键
0x51Q键
0x52R键
0x53S 键
0x54T键
0x55U键
0x56V键
0x57W键
0x58X键
0x59Y 键
0x5AZ键
VK_LWIN0x5B左 Windows 键
VK_RWIN0x5C右 Windows 键
VK_APPS0x5D应用程序密钥
-0x5E保留
VK_SLEEP0x5F计算机睡眠键
VK_NUMPAD00x60数字键盘 0 键
VK_NUMPAD10x61数字键盘 1 键
VK_NUMPAD20x62数字键盘 2 键
VK_NUMPAD30x63数字键盘 3 键
VK_NUMPAD40x64数字键盘 4 键
VK_NUMPAD50x65数字键盘 5 键
VK_NUMPAD60x66数字键盘 6 键
VK_NUMPAD70x67数字键盘 7 键
VK_NUMPAD80x68数字键盘 8 键
VK_NUMPAD90x69数字键盘 9 键
VK_MULTIPLY0x6A乘法键
VK_ADD0x6B添加密钥
VK_SEPARATOR0x6C分隔键
VK_SUBTRACT0x6D减去键
VK_DECIMAL0x6E十进制键
VK_DIVIDE0x6F分割键
VK_F10x70F1 键
VK_F20x71F2 键
VK_F30x72F3 键
VK_F40x73F4 键
VK_F50x74F5 键
VK_F60x75F6 键
VK_F70x76F7 键
VK_F80x77F8 键
VK_F90x78F9 键
VK_F100x79F10 键
VK_F110x7AF11 键
VK_F120x7BF12 键
VK_F130x7CF13 键
VK_F142岳F14 键
VK_F150x7EF15 键
VK_F160x7FF16 键
VK_F170x80F17 键
VK_F180x81F18 键
VK_F190x82F19 键
VK_F200x83F20 键
VK_F210x84F21 键
VK_F220x85F22 键
VK_F230x86F23 键
VK_F240x87F24 键
-0x88-8楼保留
VK_NUMLOCK0x90NUM LOCK 键
VK_SCROLL0x91SCROLL LOCK键
-0x92-96OEM 特定
-0x97-9楼未分配
VK_LSHIFT0xA0左 SHIFT 键
VK_RSHIFT0xA1右 SHIFT 键
VK_LCONTROL0xA2左 CONTROL 键
VK_RCONTROL0xA3右 CONTROL 键
VK_LMENU0xA4左 Alt 键
VK_RMENU0xA5右 Alt 键
VK_BROWSER_BACK0xA6浏览器后退键
VK_BROWSER_FORWARD0xA7浏览器转发键
VK_BROWSER_REFRESH0xA8浏览器刷新键
VK_BROWSER_STOP0xA9浏览器停止键
VK_BROWSER_SEARCH0xAA浏览器搜索键
VK_BROWSER_FAVORITES0xAB浏览器收藏夹键
VK_BROWSER_HOME0xAC浏览器“开始”和“主页”键
VK_VOLUME_MUTE0xAD音量静音键
VK_VOLUME_DOWN0xAE降低音量键
VK_VOLUME_UP0xAF音量调高键
VK_MEDIA_NEXT_TRACK0xB0下一曲目键
VK_MEDIA_PREV_TRACK0xB1上一页 Track 键
VK_MEDIA_STOP0xB2停止媒体键
VK_MEDIA_PLAY_PAUSE0xB3播放/暂停媒体键
VK_LAUNCH_MAIL0xB4启动邮件密钥
VK_LAUNCH_MEDIA_SELECT0xB5选择媒体密钥
VK_LAUNCH_APP10xB6启动应用程序 1 键
VK_LAUNCH_APP20xB7启动应用程序 2 键
-0xB8-B9型保留
VK_OEM_10xBA用于杂项字符;它可能因键盘而异。对于美标键盘,按键;:
VK_OEM_PLUS0xBB对于任何国家/地区,关键+
VK_OEM_COMMA0xBC对于任何国家/地区,关键,
VK_OEM_MINUS0xBD对于任何国家/地区,关键-
VK_OEM_PERIOD0xBE对于任何国家/地区,关键.
VK_OEM_20xBF用于杂项字符;它可能因键盘而异。对于美标键盘,按键/?
VK_OEM_30xC0用于杂项字符;它可能因键盘而异。对于美标键盘,按键`~
-0xC1-DA保留
VK_OEM_40xDB用于杂项字符;它可能因键盘而异。对于美标键盘,按键[{
VK_OEM_50xDC用于杂项字符;它可能因键盘而异。对于美标键盘,按键\\|
VK_OEM_60xDD用于杂项字符;它可能因键盘而异。对于美标键盘,按键]}
VK_OEM_70xDE用于杂项字符;它可能因键盘而异。对于美标键盘,按键'"
VK_OEM_80xDF用于杂项字符;它可能因键盘而异。
-0xE0保留
-0xE1OEM 特定
VK_OEM_1020xE2美国标准键盘上的键,或非美国 102 键键盘上的键<>\\|
-0xE3-E4型OEM 特定
VK_PROCESSKEY0xE5IME PROCESS 密钥
-0xE6OEM 特定
VK_PACKET0xE7用于传递 Unicode 字符,就好像它们是击键一样。键是用于非键盘输入法的 32 位虚拟键值的低位字。有关详细信息,请参阅 KEYBDINPUT、SendInput、WM_KEYDOWN 和 WM_KEYUP 中的备注VK_PACKET
-0xE8未分配
-0xE9-F5型OEM 特定
VK_ATTN0xF6收件人键
VK_CRSEL0xF7CrSel 密钥
VK_EXSEL0xF8ExSel 密钥
VK_EREOF0xF9擦除EOF密钥
VK_PLAY0xFA播放键
VK_ZOOM0xFB缩放键
VK_NONAME0xFC保留
VK_PA10xFDPA1 密钥
VK_OEM_CLEAR0xFE清除键

 对于左键,如果按了左键还要判断现在方向是不是朝右,然后才能把方向改为左边,因为朝右是绝对改变不了方向为左边的

if (KEY_PRESS(VK_LEFT) && ps->Dir != right)ps->Dir = left;

 对于右键,如果按了右键还要判断现在方向是不是朝左,然后才能把方向改为右边,因为朝左是绝对改变不了方向为右边的

if (KEY_PRESS(VK_RIGHT) && ps->Dir != left)ps->Dir = right;

方向为上和下也是同理,不能产生冲突

if (KEY_PRESS(VK_UP) && ps->Dir != down)ps->Dir = up;
if (KEY_PRESS(VK_DOWN) && ps->Dir != up)ps->Dir = down; 

 具体方向键的移动另外做了一个函数snakemove(ps);

方向键设置完了,现在设置功能键 

我是打算把空格键设置为暂停,Sleep是修眠函数,可以设置一个死循环,如果按了空格键就进入循环休眠,再按一次空格键就break跳出循环停止休眠。这样就达到暂停的效果了

if (KEY_PRESS(VK_SPACE))
{while (1){Sleep(200);if (KEY_PRESS(VK_SPACE))break;}
}

然后是退出ESC键,只需要在按这个键的时候,把状态从ok改为end就行了

if (KEY_PRESS(VK_ESCAPE))ps->statues = end;
if (KEY_PRESS(VK_F4))
{if (ps->sleeptime < 350){ps->sleeptime += 20;ps->foodweight -= 2;if (ps->sleeptime >= 350){ps->foodweight = 1;}}
}

snakemove移动函数

上面解决了按键问题,现在来解决一下按键对应的移动。先新建一个nextnode节点,这是下一步产生的节点

对于按了左键来说(一般此时都是向上或者向下的状态按左键),nextnode的x坐标要蛇头节点x-2,因为每次都是蛇头移动,而纵坐标y是保持不变的。

if (ps->Dir == left)
{nextnode->x = ps->psnake->x - 2;nextnode->y = ps->psnake->y;
}

对于按了右键来说(一般此时也都是向上或者向下的状态按右键),而nextnode的x坐标是加2,y轴依旧是不变的

if (ps->Dir == right)
{nextnode->x = ps->psnake->x + 2;nextnode->y = ps->psnake->y;
}

对于按了上键来说(一般此时蛇方向是向左或者向右状态),此时nextnode的x轴是不变的,y轴-1就可以了

if (ps->Dir == up)
{nextnode->y = ps->psnake->y - 1;nextnode->x = ps->psnake->x;
}

对于按了下键来说(一般此时蛇方向是向左或者向右状态),此时nextnode的x轴是不变的,y轴+1就可以了

if (ps->Dir == down)
{nextnode->y = ps->psnake->y + 1;nextnode->x = ps->psnake->x;
}

解决了下一个节点的坐标问题,然后来考虑下一个节点正好是食物节点以及不是食物节点的情况。判断是否是食物节点很好判断,直接if (nextnode->x == ps->pfood->x && nextnode->y == ps->pfood->y)

对于下一个节点是食物,我另外做了一个函数eatfood();如果下一个节点是食物,那直接头插法插进蛇身链表就可以了,同时蛇头变为这个食物节点,然后以新的食物节点作为头结点打印蛇身。  完了之后,因为吃了食物,所以总分数score要加上食物的分数foodweight。最后删除原有的食物节点,重新初始化一个食物节点

void eatfood(Snake* ps, snakenode* nextnode)
{nextnode->next= ps->psnake;ps->psnake =nextnode;snakenode* cur = ps->psnake;//头插食物节点while (cur){setpos(cur->x, cur->y);wprintf(L"%lc", BODY);cur = cur->next;//打印蛇身}ps->score += ps->foodweight;//分数更新free(ps->pfood);//释放旧的食物节点foodInit(ps);//新建食物节点
}//吃食物

对于下一个位置不是食物节点,首先下一个位置也要成为新头节点,然后打印蛇身,但是最后一个节点要打印空格,这样看上去就像删除了最后一个节点一样。

void notfood(Snake*ps, snakenode*nextnode)
{nextnode->next = ps->psnake;ps->psnake = nextnode;snakenode* cur = ps->psnake;while (cur->next->next)//找到最后一个节点{setpos(cur->x, cur->y);wprintf(L"%lc", BODY);cur = cur->next;}setpos(cur->next->x, cur->next->y);printf("  ");//这个空格是两个空格free(cur->next);cur->next = NULL;
}

撞墙机制

因为我之前已经枚举了游戏状态,有一个状态是killbywall,所以只有判断蛇头是不是和四面墙的坐标重叠,你也可以是刚碰到墙不是重叠就判断撞墙

void _killbywall(Snake* ps)
{if (ps->psnake->x== 0 || ps->psnake->x == 54 || ps->psnake->y == 0 || ps->psnake->y == 27)ps->statues = killbywall;
}//撞墙

咬到自己机制 

同样也是用蛇头去判断,我选取的是蛇头的next节点以及之后的节点,作为咬到的判断条件。然后把状态改成killbyself

void _killbyself(Snake* ps)
{snakenode* cur = ps->psnake->next;while (cur){if (ps->psnake->x == cur->x && ps->psnake->y == cur->y){ps->statues = killbyself;break;}cur = cur->next;}
}//咬到自己

整个游戏的运行阶段就结束了,因为F3和F4按键是运行阶段设置的,所以我把打印分数和食物分数放到了 gamerun(Snake* ps)函数里,同时要注意的是每次按键都要设置休眠时间,不然的话会卡顿。

游戏运行阶段的全部代码如下,从下往上看

//游戏运行阶段void _killbywall(Snake* ps)
{if (ps->psnake->x== 0 || ps->psnake->x == 54 || ps->psnake->y == 0 || ps->psnake->y == 27)ps->statues = killbywall;
}//撞墙void _killbyself(Snake* ps)
{snakenode* cur = ps->psnake->next;while (cur){if (ps->psnake->x == cur->x && ps->psnake->y == cur->y){ps->statues = killbyself;break;}cur = cur->next;}
}//咬到自己void eatfood(Snake* ps, snakenode* nextnode)
{nextnode->next= ps->psnake;ps->psnake =nextnode;snakenode* cur = ps->psnake;//头插食物节点while (cur){setpos(cur->x, cur->y);wprintf(L"%lc", BODY);cur = cur->next;//打印蛇身}ps->score += ps->foodweight;//分数更新free(ps->pfood);//释放旧的食物节点foodInit(ps);//新建食物节点
}//吃食物void notfood(Snake*ps, snakenode*nextnode)
{nextnode->next = ps->psnake;ps->psnake = nextnode;snakenode* cur = ps->psnake;while (cur->next->next)//找到最后一个节点{setpos(cur->x, cur->y);wprintf(L"%lc", BODY);cur = cur->next;}setpos(cur->next->x, cur->next->y);printf("  ");//这个空格是两个空格free(cur->next);cur->next = NULL;
}
void snakemove(Snake* ps)
{snakenode* nextnode = (snakenode*)malloc(sizeof(snakenode));if (ps->Dir == left){nextnode->x = ps->psnake->x - 2;nextnode->y = ps->psnake->y;}if (ps->Dir == right){nextnode->x = ps->psnake->x + 2;nextnode->y = ps->psnake->y;}if (ps->Dir == up){nextnode->y = ps->psnake->y - 1;nextnode->x = ps->psnake->x;}if (ps->Dir == down){nextnode->y = ps->psnake->y + 1;nextnode->x = ps->psnake->x;}if (nextnode->x == ps->pfood->x && nextnode->y == ps->pfood->y)eatfood(ps, nextnode);else{notfood(ps, nextnode);}_killbywall(ps);_killbyself(ps);
}void gamerun(Snake* ps)
{do{setpos(61, 8);printf("总分:%3d", ps->score);setpos(70, 8);printf("食物的分数:%02d", ps->foodweight);if (KEY_PRESS(VK_LEFT) && ps->Dir != right)ps->Dir = left;if (KEY_PRESS(VK_RIGHT) && ps->Dir != left)ps->Dir = right;if (KEY_PRESS(VK_UP) && ps->Dir != down)ps->Dir = up;if (KEY_PRESS(VK_DOWN) && ps->Dir != up)ps->Dir = down;if (KEY_PRESS(VK_SPACE)){while (1){Sleep(200);if (KEY_PRESS(VK_SPACE))break;}}if (KEY_PRESS(VK_ESCAPE))ps->statues = end;if (KEY_PRESS(VK_F3)){if (ps->sleeptime >=50){ps->sleeptime -= 20;ps->foodweight += 2;}}if (KEY_PRESS(VK_F4)){if (ps->sleeptime < 350){ps->sleeptime += 20;ps->foodweight -= 2;if (ps->sleeptime >= 350){ps->foodweight = 1;}}}Sleep(ps->sleeptime);snakemove(ps);} while (ps->statues == ok);
}

四.游戏结束阶段

游戏结束阶段就是收尾阶段,把各种死亡原因打印一下,告知为什么死的,然后释放蛇身节点

void endgame(Snake* ps)
{if (ps->statues==end){setpos(20, 13);printf("您主动已经退出了游戏\n");}else if (ps->statues == killbyself){setpos(20, 13);printf("您咬到了自己\n");}else if (ps->statues == killbywall){setpos(20, 13);printf("您撞墙了\n");}while (ps->psnake){snakenode* cur = ps->psnake->next;free(ps->psnake);ps->psnake = cur;}ps = NULL;
}//游戏结束阶段

然后我还在main那里加了一个循环以便于重新开始游戏

#include"贪食蛇的声明.h"
void text()
{char ch = 0;do{setlocale(LC_ALL, "");gamestart();Snake ps = {0};//里面的成员先全赋值为0snakeInit(&ps);foodInit(&ps);gamerun(&ps);endgame(&ps);setpos(20, 15);printf("再来一局吗?(Y/N):");//Y是重新开始scanf("%c", &ch);getchar();// 清理\n
} while (ch == 'Y' || ch == 'y');
}
int main()
{srand((unsigned int)time(NULL));text();
}

五.贪食蛇完整代码

测试.c文件代码

#include"贪食蛇的声明.h"
void text()
{char ch = 0;do{setlocale(LC_ALL, "");gamestart();Snake ps = {0};//里面的成员先全赋值为0snakeInit(&ps);foodInit(&ps);gamerun(&ps);endgame(&ps);setpos(20, 15);printf("再来一局吗?(Y/N):");//Y是重新开始scanf("%c", &ch);getchar();// 清理\n
} while (ch == 'Y' || ch == 'y');
}
int main()
{srand((unsigned int)time(NULL));text();
}

贪食蛇的声明.h文件

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#define KEY_PRESS(VK) ((GetAsyncKeyState(VK)&0x1) ? 1 : 0)
#include<stdio.h>
#include<windows.h>
#include<stdbool.h>
#include<locale.h>
#include<time.h>
#define wall L'□'
#include<stdlib.h>
#define BODY L'■'
#define FOOD L'★'
void setpos(int x, int y);
void gamestart();enum statue
{ok = 1,end,killbywall,//撞墙killbyself//咬到自己
};enum direction
{up=1,down,left,right
};
typedef  struct snakenode
{int x;int y;struct snakenode* next;
}snakenode;//蛇身节点和食物节点typedef struct snakegame
{snakenode* psnake;//蛇身体节点snakenode *pfood;//食物节点int score;//总分int foodweight;//食物分数enum statue statues;//游戏状态enum direction Dir;//方向int sleeptime;//睡眠时间
}Snake;//维护蛇的结构体void snakeInit(Snake* ps);
void foodInit(Snake* ps);
void gamerun(Snake* ps);
void snakemove(Snake* ps);
void endgame(Snake* ps);

贪食蛇的实现.h代码

#include"贪食蛇的声明.h"
void setpos(int x, int y)
{COORD pos = { x,y };HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);SetConsoleCursorPosition(houtput, pos);}
void welcome()
{setpos(40, 10);printf("欢迎来到贪食蛇小游戏\n");setpos(43, 15);system("pause");system("cls");//清屏setpos(38, 10);printf("用 ↑ ↓ → ←键操作蛇\n");setpos(38, 13);printf("f3表示加速,f4表示减速,加速能得到更多的分数");setpos(38, 18);system("pause");system("cls");}
void createmap()
{for (int i = 0; i < 56; i += 2){setpos(i, 0);wprintf(L"%lc", wall);}//打印第一个横着的墙for (int i = 1; i < 28; i++){setpos(0, i);wprintf(L"%lc", wall);}//打印最左边竖着的墙for (int i = 2; i < 56; i += 2){setpos(i, 27);wprintf(L"%lc", wall);}//最下面横着的墙for (int i = 1; i < 27; i++){setpos(54, i);wprintf(L"%lc", wall);}setpos(61, 10);printf("用 ↑ ↓ → ←键操作蛇\n");setpos(61, 12);printf("f3表示加速,f4表示减速\n");setpos(61, 15);printf("加速能得到更多的分数\n");//打印墙体旁边的提示信息setpos(61, 17);printf("esc键退出游戏\n");setpos(61, 19);printf("空格键暂停\n");
}void gamestart()
{system("title 贪食蛇");system("mode con cols=100 lines=30");HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);CONSOLE_CURSOR_INFO Info;GetConsoleCursorInfo(houtput, &Info);//获取光标控制台信息Info.bVisible = 0;//隐藏光标SetConsoleCursorInfo(houtput, &Info);//把修改后的结果设置回去welcome();createmap();
}void snakeInit(Snake* ps)
{for (int i = 0; i <10; i+=2){snakenode* cur = (snakenode*)malloc(sizeof(snakenode));if (ps->psnake == NULL){cur->next = ps->psnake;ps->psnake = cur;}else{cur->next = ps->psnake;ps->psnake = cur;}cur->x = 24 + i;cur->y = 5;//设置cur的x和y坐标setpos(cur->x, cur->y);//在cur坐标上打印蛇的身体wprintf(L"%lc", BODY);}ps->Dir = right;//方向ps->foodweight = 10;//一个食物的分数ps->score = 0;//总分ps->sleeptime = 200;//休眠时间ps->statues = ok;//游戏状态
}//蛇身节点初始化void foodInit(Snake* ps)
{int x; int y;
again:do {x = rand()%51 + 2;y = rand()%26 + 1;//控制节点坐标生成在墙内} while (x % 2 != 0);//宽字符x必须是2的倍数snakenode* cur = ps->psnake;while (cur){if (x == cur->x &&y == cur->y)//判断生成的坐标是否是蛇身节点goto again;//如果是蛇身节点那么就跳转回上面重新生成x,ycur = cur->next;}ps->pfood = (snakenode*)malloc(sizeof(snakenode));//pfood是维护食物节点的指针ps->pfood->x = x; ps->pfood->y = y;setpos(ps->pfood->x, ps->pfood->y);wprintf(L"%lc", FOOD);
}//食物节点初始化//游戏运行阶段void _killbywall(Snake* ps)
{if (ps->psnake->x== 0 || ps->psnake->x == 54 || ps->psnake->y == 0 || ps->psnake->y == 27)ps->statues = killbywall;
}//撞墙void _killbyself(Snake* ps)
{snakenode* cur = ps->psnake->next;while (cur){if (ps->psnake->x == cur->x && ps->psnake->y == cur->y){ps->statues = killbyself;break;}cur = cur->next;}
}//咬到自己void eatfood(Snake* ps, snakenode* nextnode)
{nextnode->next= ps->psnake;ps->psnake =nextnode;snakenode* cur = ps->psnake;//头插食物节点while (cur){setpos(cur->x, cur->y);wprintf(L"%lc", BODY);cur = cur->next;//打印蛇身}ps->score += ps->foodweight;//分数更新free(ps->pfood);//释放旧的食物节点foodInit(ps);//新建食物节点
}//吃食物void notfood(Snake*ps, snakenode*nextnode)
{nextnode->next = ps->psnake;ps->psnake = nextnode;snakenode* cur = ps->psnake;while (cur->next->next)//找到最后一个节点{setpos(cur->x, cur->y);wprintf(L"%lc", BODY);cur = cur->next;}setpos(cur->next->x, cur->next->y);printf("  ");//这个空格是两个空格free(cur->next);cur->next = NULL;
}
void snakemove(Snake* ps)
{snakenode* nextnode = (snakenode*)malloc(sizeof(snakenode));if (ps->Dir == left){nextnode->x = ps->psnake->x - 2;nextnode->y = ps->psnake->y;}if (ps->Dir == right){nextnode->x = ps->psnake->x + 2;nextnode->y = ps->psnake->y;}if (ps->Dir == up){nextnode->y = ps->psnake->y - 1;nextnode->x = ps->psnake->x;}if (ps->Dir == down){nextnode->y = ps->psnake->y + 1;nextnode->x = ps->psnake->x;}if (nextnode->x == ps->pfood->x && nextnode->y == ps->pfood->y)eatfood(ps, nextnode);else{notfood(ps, nextnode);}_killbywall(ps);_killbyself(ps);
}void gamerun(Snake* ps)
{do{setpos(61, 8);printf("总分:%3d", ps->score);setpos(70, 8);printf("食物的分数:%02d", ps->foodweight);if (KEY_PRESS(VK_LEFT) && ps->Dir != right)ps->Dir = left;if (KEY_PRESS(VK_RIGHT) && ps->Dir != left)ps->Dir = right;if (KEY_PRESS(VK_UP) && ps->Dir != down)ps->Dir = up;if (KEY_PRESS(VK_DOWN) && ps->Dir != up)ps->Dir = down;if (KEY_PRESS(VK_SPACE)){while (1){Sleep(200);if (KEY_PRESS(VK_SPACE))break;}}if (KEY_PRESS(VK_ESCAPE))ps->statues = end;if (KEY_PRESS(VK_F3)){if (ps->sleeptime >=50){ps->sleeptime -= 20;ps->foodweight += 2;}}if (KEY_PRESS(VK_F4)){if (ps->sleeptime < 350){ps->sleeptime += 20;ps->foodweight -= 2;if (ps->sleeptime >= 350){ps->foodweight = 1;}}}Sleep(ps->sleeptime);snakemove(ps);} while (ps->statues == ok);
}
void endgame(Snake* ps)
{if (ps->statues==end){setpos(20, 13);printf("您主动已经退出了游戏\n");}else if (ps->statues == killbyself){setpos(20, 13);printf("您咬到了自己\n");}else if (ps->statues == killbywall){setpos(20, 13);printf("您撞墙了\n");}while (ps->psnake){snakenode* cur = ps->psnake->next;free(ps->psnake);ps->psnake = cur;}ps = NULL;
}//游戏结束阶段

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/672507.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【学习笔记】TypeScript学习笔记1 --TypeScript中的类型

文章目录 TS总的变量类型References TS总的变量类型 备注&#xff1a; 如果一个变量设置为了any 类型之后相当于变量关闭了TS的类型检测 let d: any; d 10; d hello;//unknown表示的是未知类型&#xff0c;实际是上一个安全的any,unknown类型的变量不能直接赋值给其他变量le…

【Linux】基于管道进行进程间通信

进程间通信 一、初识进程间通信1. 进程间通信概念2. 进程间通信分类 二、管道1. 管道概念2. 管道原理3. 匿名管道4. 匿名管道系统接口5. 管道的特性和情况6. 匿名管道的应用&#xff08;1&#xff09;命令行&#xff08;2&#xff09;进程池 7. 命名管道&#xff08;1&#xff…

Linux------命令行参数

目录 前言 一、main函数的参数 二、命令行控制实现计算器 三、实现touch指令 前言 当我们在命令行输入 ls -al &#xff0c;可以查看当前文件夹下所有文件的信息&#xff0c;还有其他的如rm&#xff0c;touch等指令&#xff0c;都可以帮我们完成相应的操作。 其实运行这些…

【语音合成】中文-多情感领域-16k-多发音人

模型介绍 语音合成-中文-多情感领域-16k-多发音人 框架描述 拼接法和参数法是两种Text-To-Speech(TTS)技术路线。近年来参数TTS系统获得了广泛的应用&#xff0c;故此处仅涉及参数法。 参数TTS系统可分为两大模块&#xff1a;前端和后端。 前端包含文本正则、分词、多音字预…

【C++】构造函数、初始化列表,析构函数,拷贝构造函数,运算符重载

注&#xff1a;本博客图片来源于学习笔记: 学习笔记https://gitee.com/box-he-he/learning-notes 完整思维导图请前往该博主码云下载。 目录 注&#xff1a;本博客图片来源于学习笔记: 学习笔记https://gitee.com/box-he-he/learning-notes 完整思维导图请前往该博主码云下载…

2024无参考图像的清晰度评价方法

无参考图像质量评价算法 无参考图像质量评价是指参考图像不存在的情况下&#xff0c;直接计算失真图像的视觉质量。根据无参考图像质量评价模型在计算图像视觉质量时是否需要图像的主观分数来进行训练&#xff0c;无参考图像质量评价算法可分为基于监督学习的无参考图像质量评价…

2024-02-06 TCP/UDP work

1. 画出TCP三次握手和四次挥手的示意图&#xff0c;并且总结TCP和UDP的区别 三次握手&#xff1a; 4次挥手&#xff1a; tcp/udp区别 TCP 1. 稳定&#xff0c;提供面向连接的&#xff0c;可靠的数据传输服务 2. 传输过程中&#xff0c;数据无误、数据无丢失、数据无失序、…

IDEA 配置以及一些技巧

1. IDEA设置 1.1 设置主题 1.2 设置字体和字体大小 1.3 编辑区的字体用ctrl鼠标滚轮可以控制大小 1.4 自动导包和优化多余的包 1.5 设置编码方式 1.6 配置 maven 1.7 设置方法形参参数提示 1.8 设置控制台的字体和大小 注意&#xff1a;设置控制台字体和大小后需要重启IDEA才会…

第1章 认识Flask

学习目标 了解Flask框架&#xff0c;能够说出Flask框架的发展史以及特点 熟悉隔离Python环境的创建方式&#xff0c;能够独立在计算机上创建隔离的Python环境 掌握Flask的安装方式&#xff0c;能够独立在计算机上安装Flask框架 掌握PyCharm配置隔离环境的方式&#xff0c;能…

【C++】基础知识讲解(命名空间、缺省参数、重载、输入输出)

&#x1f308;个人主页&#xff1a;秦jh__https://blog.csdn.net/qinjh_?spm1010.2135.3001.5343&#x1f525; 系列专栏&#xff1a;http://t.csdnimg.cn/eCa5z 目录 命名空间 命名空间的定义 命名空间的使用 命名空间的嵌套使用 C输入&输出 std命名空间的使用惯例&…

C语言第二十弹---指针(四)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 指针 1、字符指针变量 2、数组指针变量 2.1、数组指针变量是什么&#xff1f; 2.2、数组指针变量怎么初始化 3、⼆维数组传参的本质 4、函数指针变量 4.1…

Rust开发WASM,浏览器运行WASM

首先需要安装wasm-pack cargo install wasm-pack 使用cargo创建工程 cargo new --lib mywasm 编辑Cargo.toml文件&#xff0c;修改lib的类型为cdylib&#xff0c;并且添加依赖wasm-bindgen [package] name "mywasm" version "0.1.0" edition "…

二进制安全虚拟机Protostar靶场(8)heap3 Fastbins unlink exploit

前言 这是一个系列文章&#xff0c;之前已经介绍过一些二进制安全的基础知识&#xff0c;这里就不过多重复提及&#xff0c;不熟悉的同学可以去看看我之前写的文章 heap3 程序静态分析 https://exploit.education/protostar/heap-three/#include <stdlib.h> #include …

故障诊断 | 一文解决,TCN时间卷积神经网络模型的故障诊断(Matlab)

效果一览 文章概述 故障诊断 | 一文解决,TCN时间卷积神经网络模型的故障诊断(Matlab) 模型描述 时间卷积神经网络(TCN)是一种用于序列数据建模和预测的深度学习模型。它通过卷积操作在时间维度上对序列数据进行特征提取,并且可以处理可变长度的输入序列。 要使用TCN进行…

3D室内虚拟灭火体验为预防火灾提供全新方案

室内火灾常见于充电器未拔、电动车、油锅起火及煤气泄露等原因&#xff0c;由于室内空间小、易燃物多&#xff0c;因此极易造成较大的人员财产损失&#xff0c;3D仿真还原技术、通过1&#xff1a;1模拟还原火灾发生全过程&#xff0c;火灾VR安全培训提供全方位、真实感强的模拟…

FlinkSql 窗口函数

Windowing TVF 以前用的是Grouped Window Functions&#xff08;分组窗口函数&#xff09;&#xff0c;但是分组窗口函数只支持窗口聚合 现在FlinkSql统一都是用的是Windowing TVFs&#xff08;窗口表值函数&#xff09;&#xff0c;Windowing TVFs更符合 SQL 标准且更加强大…

Java中JVM常用参数配置(提供配置示例)

目录 前言一、内存参数配置二、垃圾收集器配置三、GC策略配置3.1、基础通用配置3.2、Parallel 和 Parallel Old 常用参数配置3.3、CMS 常用参数配置3.4、G1 常用参数配置 四、GC日志配置五、dump 日志参数配置5.1、OutOfMemory异常时生成dump文件5.2、发生Full GC时生成dump文件…

QT安装与helloworld

文章目录 QT安装与helloworld1.概念&#xff1a;2.安装QT3.配置环境变量4.创建项目5.运行效果 QT安装与helloworld 1.概念&#xff1a; Qt Creator是一个用于Qt开发的轻量级跨平台集成开发环境。Qt Creator可带来两大关键益处&#xff1a;提供首个专为支持跨平台开发而设计的…

在Python中执行Linux Shell脚本详解

概要 随着 Python 的增长和普及,目前它已经成为自动化各种任务,包括执行 shell 脚本的主要工具。这篇文章将详细描述如何在 Python 中执行 shell 脚本,并提供丰富的示例帮助你理解和实践。 什么是Shell脚本? Shell脚本是一个由命令行解释器执行的文本文件。这些脚本包含控…

数据结构第九天(堆排序)

目录 前言 概述 源码&#xff1a; 主函数&#xff1a; 运行结果&#xff1a; 其他 前言 哈哈&#xff0c;这个堆排序算法很久之前就已经敲过一遍了&#xff0c;时间一久&#xff0c;思路有点淡忘。今天重新看过一遍之后&#xff0c;又亲自撸代码&#xff0c;幸运的是&am…