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,y
		cur = 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,y
		cur = 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};//里面的成员先全赋值为0
	snakeInit(&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};//里面的成员先全赋值为0
	snakeInit(&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,y
		cur = 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;
}//游戏结束阶段

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

相机图像质量研究(5)常见问题总结:光学结构对成像的影响--景深

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…

Sentiment AI:智能化加密时代的引领者

“或许在不久&#xff0c;通过智能化投资分析工具做投资决策&#xff0c;将成为加密投资者必备的手段&#xff0c;而Sentiment AI 正在加速智能化加密时代大门的开启。” 在加密货币领域中&#xff0c;只有极少一部分人购买加密货币是因为被文化所吸引&#xff0c;绝大多数链上…

C# CAD交互界面-自定义工具栏(二)

运行环境 vs2022 c# cad2016 调试成功 一、引用 acdbmgd.dllacmgd.dllaccoremgd.dllAutodesk.AutoCAD.Interop.Common.dllAutodesk.AutoCAD.Interop.dll using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.T…

23.HarmonyOS App(JAVA)堆叠布局StackLayout使用方法

不常用 StackLayout直接在屏幕上开辟出一块空白的区域&#xff0c;添加到这个布局中的视图都是以层叠的方式显示&#xff0c;而它会把这些视图默认放到这块区域的左上角&#xff0c;第一个添加到布局中的视图显示在最底层&#xff0c;最后一个被放在最顶层。上一层的视图会覆盖…

【蓝桥杯冲冲冲】 [SCOI2005] 骑士精神

蓝桥杯备赛 | 洛谷做题打卡day28 文章目录 蓝桥杯备赛 | 洛谷做题打卡day28[SCOI2005] 骑士精神题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1提示题解代码我的一些话[SCOI2005] 骑士精神 题目描述 输入格式 第一行有一个正整数 T T T( T ≤ 10

SDWAN组网一般会需要哪些设备

随着企业网络需求的不断增长和变化&#xff0c; SD-WAN作为一种先进的网络架构技术&#xff0c;需要一系列设备来构建有效的组网。以下是SD-WAN组网所需的一些关键设备清单&#xff1a; &#xff08;1&#xff09;SD-WAN路youqi&#xff1a; SD-WAN路youqi是SD-WAN网络的核心设…

Python Matplotlib安装过程详解

Matplotlib 是一个非常优秀的 Python 2D 绘图库&#xff0c;只要给出符合格式的数据&#xff0c;通过 Matplotlib 就可以方便地制作折线图、柱状图、散点图等各种高质量的数据图。 安装 Matplotlib 包与安装其他 Python 包没有区别&#xff0c;同样可以使用 pip 来安装。 启动…

专业知识库:中小型企业必备的高效工具

在如今这个信息爆炸的时代&#xff0c;知识管理已经成为了企业运营的重要环节。特别是对于中小型企业来说&#xff0c;如何有效地管理公司内部的知识&#xff0c;提高工作效率&#xff0c;已经成为了一个亟待解决的问题。在这篇文章中&#xff0c;我将向大家介绍一种能够帮助企…

计算机服务器中了halo勒索病毒如何处理,halo勒索病毒解密数据恢复

网络技术的不断发展与应用&#xff0c;为企业的生产生活提供了极大便利&#xff0c;但网络数据安全威胁无处不在&#xff0c;近日&#xff0c;云天数据恢复中心接到某连锁超市求助&#xff0c;企业计算机服务器被halo勒索病毒攻击&#xff0c;导致计算机系统瘫痪&#xff0c;无…

2024初始Spring(并使用idea创建springweb项目)

前言 spring呢&#xff0c;以前一直是简单的了解&#xff0c;并没有利用空闲时间去进行对应的深入的学习&#xff0c;今天呢原本是打算好好学的&#xff0c;然后后来呢感觉还是太早了接触&#xff0c;打算把前面知识在过一编之后再开始 Spring介绍 Spring | Home 大家想要访…

探索Web API SpeechSynthesis:给你的网页增添声音

Web API SpeechSynthesis是一项强大的浏览器功能&#xff0c;它允许开发者将文本转换为语音&#xff0c;并通过浏览器播放出来。本文将深入探讨SpeechSynthesis的控制接口&#xff0c;包括其功能、用法和一个完整的JavaScript示例。 参考资料&#xff1a;SpeechSynthesis - Web…

鸿蒙实战开发-全局UI方法的功能

主要开发内容 时间调节 使用全局UI的方法定义日期滑动选择器弹窗并弹出。 操作说明&#xff1a;首先创建一个包含按钮的用户界面&#xff0c;当用户点击“时间设置”按钮时&#xff0c;会弹出调用TimePickerDialog组件的show方法&#xff0c;显示一个时间选择对话框&#xff…

C++二维数组

个人主页&#xff1a;PingdiGuo_guo 收录专栏&#xff1a;C干货专栏 大家好&#xff0c;我是PingdiGuo_guo&#xff0c;今天我们来学习二维数组。 文章目录 1.二维数组的概念与思想 2.二维数组和一维数组的区别 3.二维数组的特点 4.二维数组的操作 1.定义 2.初始化 1.直…

Java Character源码剖析

Character类除了封装了一个char外&#xff0c;还封装了Unicode字符级别的各种操作&#xff0c;是Java文本处理的基础。下面结合源码分析Character的贡献。 Unicode 也许你没听过Unicode&#xff0c;但应该见过UTF-8。UTF-8&#xff08;8-bit Unicode Transformation Format&a…

记一次VulnStack渗透

信息收集 netdiscover的主机发现部分不再详解&#xff0c;通过访问端口得知20001-2003端口都为web端口&#xff0c;所以优先考虑从此方向下手 外网渗透 GetShell Struct漏洞 访问2001端口后&#xff0c;插件Wappalyzer爬取得知这是一个基于Struct的web站点&#xff0c;直接…

微软Windows生态是怎么打造成功的?

&#xff08;1&#xff09;2015年Windows10&#xff1a;兼容性 我不得不再次佩服一下微软&#xff0c;Windows10是2015年出品的&#xff0c;但是仍然能正常运行绝大多数的Windows95软件&#xff0c;不用做任何的适配修改&#xff0c;连重新编译都不用&#xff0c;运行照样正常。…

游戏服务器租用多少钱一年(游戏服务器租用多少钱合适)

2024年更新腾讯云游戏联机服务器配置价格表&#xff0c;可用于搭建幻兽帕鲁、雾锁王国等游戏服务器&#xff0c;游戏服务器配置可选4核16G12M、8核32G22M、4核32G10M、16核64G35M、4核16G14M等配置&#xff0c;可以选择轻量应用服务器和云服务器CVM内存型MA3或标准型SA2实例&am…

TDengine用户权限管理

Background 官方文档关于用户管理没有很详细的介绍&#xff0c;只有零碎的几条&#xff0c;这里记录下方便后面使用。官方文档&#xff1a;https://docs.taosdata.com/taos-sql/show/#show-users 1、查看用户 show users;super 1&#xff0c;表示超级用户权限 0&#xff0c;表…

学会这一招,轻松玩转小程序自动化

♥ 前 言 app中混合应用自动化测试 会这一招&#xff0c;轻松玩转 app 中混合应用自动化测试 上次给同学们介绍了 app 中混合应用自动化怎么做&#xff0c;今天我们再来学习下&#xff0c;app 自动化之——小程序自动化。 一、环境要求 微信版本 weixin7.0.10.apk 版本&…

K8S部署Harbor镜像仓库(含离线安装包harbor-offline-installer国内下载链接)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…