当前位置: 首页 > news >正文

贪吃蛇游戏

文章目录

    • 前言
    • 一.Win32API
      • 1.1GetStdHandle
      • 1.2GetConsoleCursorInfo
      • 1.3SetConsoleCursorInfo
      • 1.4SetConsoleCursorPosition
      • 1.5GetAsyncKeyState
      • 1.6setlocale
      • 二.游戏设计
      • 三.GameStart
        • 3.1蛇的创建
        • 3.2分文件
        • 3.3控制台设置
        • 3.4WelComeToGame
        • 3.5CreateMap
        • 3.6InitSnack
        • 3.7CreateFood
      • 四.GameRun
        • 4.1GameHelpInfo
        • 4.2判断按键
        • 4.3SnackMove
      • 五.GameEnd

前言

贪吃蛇是一款人人皆知的经典游戏,本篇文章使用C语言在Windows控制台环境下简单模拟实现贪吃蛇小游戏。

这篇博文需要读者熟悉C语言的指针、结构体、枚举、动态内存管理、以及数据结构的单链表等。补充知识有Windows操作系统为应用程序提供的功能接口函数Win32 API。

一.Win32API

API对应的英文单词是(Application Programming Interface),是Windows操作系统提供的32位平台应用程序接口,就叫做Win32API,是各种接口的集合。

为了实现贪吃蛇游戏,我们需要学习一些应用程序接口,借这些接口的功能来实现游戏效果,比如光标的隐藏,光标的定位。

在这里插入图片描述

在Windows中,上面是控制台应用程序,在Linux经常被叫做终端,我们写的贪吃蛇游戏打算在这个应用程序上运行。

在这里插入图片描述

在创建空项目时,创建了一个控制台程序。(博主使用VS2019编译器)

在前面反复提控制台,就是为了告诉读者控制台是一个应用程序,而Win32API就是提供给应用程序使用的函数接口,可以达到开启视窗、描绘图形、使用周边设备等目的。

1.1GetStdHandle

GetStdHandle是一个Windows API函数,这个函数可以从标准设备(标准输入、标准输出、标准错误)中获取一个句柄,控制台是一个标准输出设备。

每个设备都有一个句柄,并且句柄值不同。使用相应设备的句柄就可以控制这个设备。

在这里插入图片描述

以下这句代码,可以获取控制台的句柄(注意包含头文件<windows.h>)

HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);

1.2GetConsoleCursorInfo

在这里插入图片描述

该函数的参数有两个(1.句柄,2.光标类型结构体变量的地址),作用是获取句柄设备上的光标信息放到结构体变量里去。

在这里插入图片描述

CONSOLE_CURSOR_INFO是Windows操作系统提供的一个描述光标的结构体类型,成员变量dwSize表示光标的大小,bVisible表示光标的可见性,当bVisible被赋值成false时,光标可以实现隐藏

创建一个光标结构体类型变量,使用GetConsoleCursorInfo这个函数获取控制台上的光标信息,放到我们创建的结构体变量中。

HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//获取控制台句柄
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(hOutput, &CursorInfo);//把控制台的光标信息存入光标结构体变量CursorInfo中

1.3SetConsoleCursorInfo

设置句柄设备上的光标。

在这里插入图片描述

第一个参数是句柄,第二个参数是光标结构体类型变量的地址。意思是:将句柄设备上的光标设置成第二个参数传的光标。

于是通过1.21.3的组合,就可以设置控制台的光标了!代码如下:

HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(hOutput, &CursorInfo);
//把可见性设为不可见,并设置进控制台
CursorInfo.bVisible = false;
SetConsoleCursorInfo(hOutput, &CursorInfo);

注意,我们必须获取控制台光标信息存到变量中,改变该变量的可见性,再设置进控制台。

不能单单创建一个光标变量,设置不可见性就设置进控制台。简而言之就是以下代码无法有效隐藏控制台光标:

HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO CursorInfo;
CursorInfo.bVisible = false;
SetConsoleCursorInfo(hOutput, &CursorInfo);//error

1.4SetConsoleCursorPosition

设置光标在控制台的位置。当我们输出一句“hello world”的时候,文字默认在左顶角(原点)开始输出,如何让文字在我们想要的位置输出呢?

为此,我们先来谈谈控制台上的坐标系吧。

在这里插入图片描述

从原点出发,向右为x轴、向下为y轴、逐渐增大。在Windows中使用COORD这个结构体来表示坐标。

在这里插入图片描述

创建这个坐标结构体类型变量,比如:

COORD pos = {3, 4};//这个坐标是3列4行的意思,x是竖,y是横。

接着使用SetConsoleCursorPosition这个函数可以做到设置光标位置,参数如下:

在这里插入图片描述

​ 在句柄对应设备上,设置COORD类型变量为打印起始点。

//封装成这个函数,传参x和y,就可以指定控制台位置输出了。
void SetPos(int x, int y)
{COORD pos = { x, y };HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);SetConsoleCursorPosition(hOutput, pos);
}
// 比如,SetPos(64, 15)把控制台光标设置在64列15行处,此时如果打印“hello world”,就不是在左顶角打印了。

1.5GetAsyncKeyState

在这里插入图片描述

这是一个判断按键情况的函数,键盘上每一个键都有一个对应的虚拟键值,请看下图:

在这里插入图片描述

在这里插入图片描述

GetAsyncKeyState函数接收一个虚拟键值,然后返回一个short类型的数值,如果这个short类型(16个bit位)的最低位为1,则说明这个虚拟键值对应的键被按过。

#define KEY_PRESS(VK) ((GetAsyncKeyState(VK) & 0x1) ? 1 : 0)

GetAsyncKeyState(VK)返回值按位与1,如果结果为真,则返回1,说明该键被按过,否则为0。

1.6setlocale

计算机语言早期是美国以英语语种设计的语言,所以对其它国家语言兼容性不好,随着国际化越来越加深,计算机语言也支持了本地化(使用者所在地区的语言习惯)比如金钱符号本地化前是$,本地化后¥,以及日期的表示习惯。

对于本文而言,最重要的是宽字符的打印。

英文字母占控制台屏幕缓冲区一个字符位,而宽字符它占两个字符位。要使用宽字符,需要使用setlocale函数设置本地环境。

在这里插入图片描述

category是setlocale的第一参数,是本地化的选项。LC_ALL将所有选项都本地化,还有其它单个选项,比如LC_TIME就只本地化时间。

locale是setlocale的第二个参数,只有两个选项,“C”或“ ”。双引号“ ”是执行本地化,而“C"是默认。

于是写上下面这句代码就可以进行本地化:

//设置本地环境setlocale(LC_ALL, "");

以上就是写贪吃蛇需要的Win32API接口函数的学习,下面进入游戏设计环节。

二.游戏设计

贪吃蛇游戏是一条蛇在围起来的地图里走动,通过↑、↓、←、→键控制蛇的行走方向,吃地图里的食物逐渐变长,分数累增的游戏。

这样,我们设计蛇头用宽字符○表示、蛇的身体用●、地图上的墙用□、食物使用★。

期望:一开始在一个大小合适的控制台窗口,在合适的位置打印信息。告诉玩家这是贪吃蛇游戏、规则介绍、打印地图、初始化蛇等。

接着是游戏运行界面,有帮助信息、总分数的变化、控制蛇在地图里走动,蛇吃食物等。

​最后游戏结束后给我们反馈游戏因为什么原因结束的,是撞墙了?还是吃到自己?

从这个期望中,我们分三部分:分别是游戏之前的准备(GameStart),游戏运行(GameRun)和游戏结束(GameEnd)。

三.GameStart

3.1蛇的创建

蛇是由一个个结点链接起来的整体,想要在控制台上准确打印出一条蛇,结点里存的信息应是用来表示坐标的,于是:

typedef struct SnackNode
{//x、y组合标识控制台上唯一的坐标int x;int y;struct SnackNode* next;
}SnackNode, *pSnackNode;

但是我们玩的是蛇,而不是一个一个结点,这时我们思考游戏在运行的时候,怎样能控制整条蛇、吃食物的总分数、蛇的速度、蛇的方向等等。

通过再封装一个结构体,完成对以上的整体控制:

enum DIRECTION
{UP = 1,DOWN,LEFT,RIGHT
};enum GAME_STATUS
{OK,//蛇正常运行END_NORMAL,//家里有事,不玩游戏了,按Esc正常退出KILL_BY_WALL,//撞墙了KILL_BY_SELF//咬到自己了
};typedef struct Snack
{pSnackNode _pSnack;//蛇头结点控制整条蛇pSnackNode _pFood;//维护食物的指针int _Score;int _FoodWeight;int _SleepTime;//控制蛇的速度enum DIRECTION _Dir;//表示蛇的行走方向enum GAME_STATUS _Status;//蛇的状态
}Snack, *pSnack;

这里的*pSnack的意思是:相当于typedef struct Snack *(指针)重定义为pSnack,上面蛇结点指针也是一样的。

可能不熟悉贪吃蛇的读者现在不太理解,为什么蛇结构里需要有维护食物的指针,往下看就明白了。

现在蛇结构蓝图设计好了,开始写代码!

3.2分文件

在这里插入图片描述

Test.c文件用来测试,Snack.c文件用来写函数实现,Snack.h文件写头文件、写函数声明以及定义标识符、宏等。

现在进入GameStart的实现部分(Snack.c),按照预先的期望在大小合适的控制台上打印欢迎信息。

3.3控制台设置

为了方便讲解,博主在程序中会加入让运行逻辑停下来的getchar()函数。

在这里插入图片描述

注意:如果读者的控制台大小无法设置,可能是控制台的设置问题,请按下面步骤进行解决。

在这里插入图片描述

title让控制台标题变为贪吃蛇只在程序运行时生效,当程序结束后,标题便不再是我们设置的了。

3.4WelComeToGame

打印欢迎界面的时候,文字在屏幕中间显示是比较好的,而且最好不要有光标在控制台上一闪一闪的。

所以在这里需要前面的两步铺垫:如何隐藏光标、如何控制光标位置。

在这里插入图片描述

void SetPos(int x, int y)
{COORD pos = { x, y };HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);SetConsoleCursorPosition(hOutput, pos);
}void WelComeToGame()
{//隐藏光标HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);CONSOLE_CURSOR_INFO Cursor;GetConsoleCursorInfo(hOutput, &Cursor);Cursor.bVisible = false;SetConsoleCursorInfo(hOutput, &Cursor);SetPos(40, 14);printf("欢迎来到贪吃蛇小游戏");SetPos(80, 27);system("pause");//清屏system("cls");SetPos(36, 14);printf("使用↑、↓、←、→键控制蛇的移动");SetPos(80, 27);system("pause");//为下面打印地图做好准备system("cls");//清完屏后,光标会到原点
}

在这里插入图片描述

system(“pause”)是让程序暂停下来,并且会自发在控制打印出“请按任意键继续. . .”,所以定位完“欢迎来到…”后,要给这句话再定位一下。

接着再介绍一下规则:

在这里插入图片描述

到这里这个函数就完成了。下面到了游戏界面,我们要打印地图。

如果行列设置不合适,显示有问题,读者可以自行设置。

3.5CreateMap

创建地图,墙使用的宽字符是□,并且要执行本地化,支持宽字符的打印。

在这里插入图片描述

宽字符占两个字符位(列),但是和ab一样都只占一行,使用wprintf打印宽字符,格式串前面加L,字符前面加L。

控制台屏幕是100列30行,那么我们就创建一个58列28行的地图吧!

在这里插入图片描述

void CreateMap()
{setlocale(LC_ALL, "");int i = 0;//上for (i = 0; i <= 56; i += 2){//#define WALL L'□'wprintf(L"%lc", WALL);}//下SetPos(0, 27);for (i = 0; i <= 56; i += 2){wprintf(L"%lc", WALL);}//左for (i = 1; i <= 26; i++){SetPos(0, i);wprintf(L"%lc", WALL);}//右for (i = 1; i <= 26; i++){SetPos(56, i);wprintf(L"%lc", WALL);}//上图中的getchar()是为了不让程序结束//结束时打印的信息会覆盖下面的墙
}
3.6InitSnack

地图创建好后,初始化出一条蛇并打印在地图上。

在这里插入图片描述

pSnackNode BuySnackNode(int x, int y)
{pSnackNode tmp = (pSnackNode)malloc(sizeof(SnackNode));if (tmp == NULL){perror("BuySnackNode malloc fail");exit(-1);}tmp->x = x;tmp->y = y;tmp->next = NULL;return tmp;
}void InitSnack(pSnack ps)
{assert(ps);//创建蛇结点并连起来成“蛇”int i = 0;pSnackNode cur = NULL;for (i = 0; i < 5; i++){//#define POS_X 24 蛇尾的起点坐标POS_X和POX_Y//#define POS_Y 5cur = BuySnackNode(POS_X + i * 2, POS_Y);//单链表头插法if (ps->_pSnack == NULL){ps->_pSnack = cur;}else{cur->next = ps->_pSnack;ps->_pSnack = cur;}}//打印蛇身cur = ps->_pSnack;int count = 0;while(cur){SetPos(cur->x, cur->y);//第一次进来打印蛇头符号if (!(count++)){	//#define HEAD L'○'wprintf(L"%lc", HEAD);}else{	//#define BODY L'●'wprintf(L"%lc", BODY);}cur = cur->next;}//其它设置ps->_pFood = NULL;//食物还没创建,先赋空指针ps->_Score = 0;ps->_FoodWeight = 10;ps->_SleepTime = 150;//程序暂停时间,控制蛇的速度ps->_Dir = RIGHT;//初始蛇的方向为右ps->_Status = OK;//蛇运行状态是正常的
}
3.7CreateFood

创建食物,本质上也是创建蛇结点,蛇吃食物身体变长相当于把食物这个结点链接到蛇上。

食物的创建是有约束的:食物的x坐标应该和蛇一样,都是2的倍数。x列0-57总共58个,每个宽字符占2个字符位,食物也是宽字符,除去0-1和56-57是墙,2到54是食物的x坐标

食物行坐标在1-26之间,宽字符行与普通字符都是占一行,0行和27行被墙占了,食物不能与墙有重叠的。

还有一点是,食物不要与蛇的身体重合并且任意生成,以上这些约束要留意,下面开始写代码:

在这里插入图片描述

void CreateFood(pSnack ps)
{assert(ps);int x = 0;int y = 0;
again:do {	//随机数的使用,在主函数里设置一次生成起点x = rand() % 53 + 2;//取模53得到0~52的余数+2->2~54y = rand() % 26 + 1;//产生1~26之间的随机数} while (x % 2 != 0);//x是奇数重新生成坐标pSnackNode cur = ps->_pSnack;while (cur){//判断蛇结点与食物结点是否重合if (cur->x == x && cur->y == y){goto again;}cur = cur->next;}//到这里,食物结点坐标合法pSnackNode FoodNode = BuySnackNode(x, y);//打印食物在地图上的位置SetPos(x, y);wprintf(L"%lc", FOOD);//#define FOOD L'★'//出了创建食物的作用域,食物结点将丢失,于是使用蛇结构食物指针维护ps->_pFood = FoodNode;
}

到了这一步,游戏的所有准备工作都做好了,进入游戏阶段。

四.GameRun

游戏玩耍阶段,控制台左边是蛇在地图里走动,右边设置分数变化和按键说明的信息,比如:按空格键暂停、按Esc键正常退出等等。

在这里插入图片描述

4.1GameHelpInfo

在这里插入图片描述

void GameHelpInfo()
{SetPos(64, 14);printf("1.使用↑.↓.←.→键控制蛇的移动");SetPos(64, 15);printf("2.按F2键加速、F3减速");SetPos(64, 16);printf("3.蛇不能撞墙,不能咬到自己");SetPos(64, 17);printf("4.Space——暂停、Esc——正常退出");SetPos(84, 24);printf("@啊苏");
}
4.2判断按键

因为分数是时刻变化的,所以获得分数不在帮助信息里打印,细想什么是跟随的游戏一直在变的呢?没错就是玩家的按键逻辑,将分数的打印放在按键里。

在这里插入图片描述

判断按键的情况在下面的代码中进行讲解:

void Pause()
{while (1){Sleep(100);if (PRESS_KEY(VK_SPACE)){break;}}
}void GameRun(pSnack ps)
{assert(ps);//打印帮助信息GameHelpInfo();//判断按键情况do{SetPos(64, 10);printf("获得的分数:%-4d", ps->_Score);SetPos(64, 11);printf("每个食物的分数:%-2d", ps->_FoodWeight);//键盘上的↑键对应的虚拟值是VK_UP这个常数if (PRESS_KEY(VK_UP) && ps->_Dir != DOWN){	//#define PRESS_KEY(VK) ((GetAsyncKeyState(VK)&0x1) ? 1 : 0)ps->_Dir = UP;}else if (PRESS_KEY(VK_DOWN) && ps->_Dir != UP){	//想让蛇的方向朝下,前提是蛇原型方向不能是朝上走的ps->_Dir = DOWN;}else if (PRESS_KEY(VK_LEFT) && ps->_Dir != RIGHT){ps->_Dir = LEFT;}else if (PRESS_KEY(VK_RIGHT) && ps->_Dir != LEFT){ps->_Dir = RIGHT;}else if (PRESS_KEY(VK_F2)){//最多加速三次(从设定的速度开始算)if (ps->_SleepTime > 30){//_SleepTime控制蛇的速度,越短蛇走的越快ps->_SleepTime -= 40;ps->_FoodWeight += 5;}}else if (PRESS_KEY(VK_F3)){//最多减速一次(从设定的速度开始算)if (ps->_SleepTime < 190){ps->_SleepTime += 40;ps->_FoodWeight -= 5;}}else if (PRESS_KEY(VK_ESCAPE)){ps->_Status = END_NORMAL;break;}else if (PRESS_KEY(VK_SPACE)){Pause();//暂停函数}} while (ps->_Status == OK);//只有蛇正常运行时,才能正常让玩家按键控制方向
}

读者可以在搜索引擎上找GetAsyncKeyState这个函数,然后里面有关于虚拟键值的超链接,点击跳转即可查看。

这么多按键是代码看起来很复杂,但其逻辑并不难理解,但cpu处理速度很快,不用担心处理不过来。

然后睡眠时间现在就起作用了。

在这里插入图片描述

在判断按键后,用于Sleep函数让程序停下来一段时间,_SleepTime = 150,150是毫秒。

在短暂的停止后,蛇开始移动!

4.3SnackMove
//判断蛇下一位置是否为结点
int NextIsFood(pSnack ps, pSnackNode pnext)
{	//食物指针的用处体现了,在游戏期间,食物指针能很好的维护着食物,防止内存泄漏if (pnext->x == ps->_pFood->x && pnext->y == ps->_pFood->y){//如果蛇结点里的食物坐标与蛇下一步走的位置相同 返回1确认是食物return 1;}else{//返回0说明蛇的下一位置不是食物return 0;}
}//注意食物结点的坐标与预创建的结点坐标一致,但不是同一个结点!
void EatFood(pSnack ps, pSnackNode pnext)
{assert(ps);//预创建的结点成为蛇头,pnext->next = ps->_pSnack;ps->_pSnack = pnext;//然后打印蛇pSnackNode cur = ps->_pSnack;int count = 0;while (cur){SetPos(cur->x, cur->y);if (!(count++)){wprintf(L"%lc", HEAD);}else{wprintf(L"%lc", BODY);}cur = cur->next;}//获得分数ps->_Score += ps->_FoodWeight;//释放食物结点,食物结点free(ps->_pFood);ps->_pFood = NULL;//重新生成食物,这个创建食物的函数会打印食物在地图上CreateFood(ps);
}void NotFood(pSnack ps, pSnackNode pnext)
{assert(ps);//不是食物也要把预创建的结点链接进来成为蛇头pnext->next = ps->_pSnack;ps->_pSnack = pnext;//把蛇尾给去掉保持长度不变,使用前后指针,打印蛇身pSnackNode cur = ps->_pSnack;pSnackNode curPrev = ps->_pSnack;int count = 0;while (cur->next){SetPos(cur->x, cur->y);if (!(count++)){wprintf(L"%lc", HEAD);}else{wprintf(L"%lc", BODY);}curPrev = cur;cur = cur->next;}//到这里cur是旧蛇尾,要把这里的宽字符打印成空格,打印空格覆盖掉地图上的宽字符SetPos(cur->x, cur->y);printf("  ");//释放旧蛇尾free(cur);//把新的蛇尾next指针赋空curPrev->next = NULL;
}void SnackMove(pSnack ps)
{assert(ps);//蛇的移动选择预开辟下一位置结点方法pSnackNode pNext = NULL;pSnackNode pHead = ps->_pSnack;//旧蛇头//根据蛇的方向,选择下一结点的位置switch (ps->_Dir){	//pNext是预创建的结点!case UP:pNext = BuySnackNode(pHead->x, pHead->y - 1);//上的话,列不变行减1break;case DOWN:pNext = BuySnackNode(pHead->x, pHead->y + 1);break;case LEFT:pNext = BuySnackNode(pHead->x - 2, pHead->y);//左是行不变,列减2,因为宽字符占两列break;case RIGHT:pNext = BuySnackNode(pHead->x + 2, pHead->y);break;}//蛇方向下一个结点有两种情况:是食物和不是食物if (NextIsFood(ps, pNext)){//是食物就把食物吃了,把pNext结点链接进蛇并成为蛇头EatFood(ps, pNext);}else{NotFood(ps, pNext);}}

到这里,玩家就能正常在使用上下左右等键控制蛇的移动,加减速、暂停等。接下来要处理蛇在运行时,状态从OK变为不Ok的逻辑。

在这里插入图片描述

void KillByWall(pSnack ps)
{assert(ps);if (ps->_pSnack->x == 0 ||ps->_pSnack->x == 56 ||ps->_pSnack->y == 0 ||ps->_pSnack->y == 27)ps->_Status = KILL_BY_WALL;
}void KillBySelf(pSnack ps)
{assert(ps);pSnackNode cur = ps->_pSnack->next;//蛇头以后的结点与蛇头相比是否有坐标一致的while (cur){if (cur->x == ps->_pSnack->x && cur->y == ps->_pSnack->y){ps->_Status = KILL_BY_SELF;break;}cur = cur->next;}
}

到这里,蛇的逻辑就完整了,但是游戏结束后,还没能得到很好的善后处理,比如链表的释放、告诉玩家是怎么游戏结束的等等。

下面进入到游戏结束环节:

五.GameEnd

游戏结束后,在合适的位置打印信息给玩家:

void GameEnd(pSnack ps)
{assert(ps);SetPos(15, 10);switch (ps->_Status){case END_NORMAL:printf("主动退出,游戏结束");break;case KILL_BY_WALL:printf("撞到墙了,游戏结束");break;case KILL_BY_SELF:printf("咬到自己,游戏结束");break;}//释放蛇pSnackNode cur = ps->_pSnack;while (cur){pSnackNode curnext = cur->next;free(cur);cur = curnext;}
}

在这里插入图片描述

程序在结束时有这句话,我们可以在程序结束前定好光标位置,让这句话不要影响游戏效果。

除了这个之外,游戏最重要的一个再来一局逻辑还没有设计,我们把最后的坑补上。

void Test()
{int input = 0;do {Snack s = { 0 };//游戏开始GameStart(&s);//游戏运行GameRun(&s);//游戏结束GameEnd(&s);SetPos(15, 12);printf("是否想要再来一局(Y/N):");input = getchar();getchar();//清空输入缓冲区}while(input == 'Y' || input == 'y');SetPos(0, 28);
}

到这里,贪吃蛇小游戏实现完毕!

希望这篇博文对想实现贪吃蛇小游戏的读者有帮助。

相关文章:

贪吃蛇游戏

文章目录 前言一.Win32API1.1GetStdHandle1.2GetConsoleCursorInfo1.3SetConsoleCursorInfo1.4SetConsoleCursorPosition1.5GetAsyncKeyState1.6setlocale二.游戏设计三.GameStart3.1蛇的创建3.2分文件3.3控制台设置3.4WelComeToGame3.5CreateMap3.6InitSnack3.7CreateFood 四.…...

DPDK trace 的简单使用

文章目录 前言trace的简单使用 前言 日志用于记录不太频繁&#xff0c;比较高level的事情。trace记录频繁发生的事情&#xff0c;它的开销低。 trace可以在运行时&#xff0c;通过参数控制是否启用&#xff1b;可以在任何时间点&#xff0c;将trace记录的缓冲区保存到文件系统…...

《游戏-01_2D-开发》

首先利用安装好的Unity Hub创建一个unity 2D&#xff08;URP渲染管线&#xff09;项目 选择个人喜欢的操作格局&#xff08;这里采用2 by 3&#xff09; 在Project项目管理中将双栏改为单栏模式&#xff08;个人喜好&#xff09; 找到首选项&#xff08;Preferences&#xff09…...

如何禁用WordPress站点的管理员电子邮件验证或修改检查频率?

今天boke112百科登录某个WordPress站点时&#xff0c;又出现“管理员邮件确认”的提示&#xff0c;要求确认此站点的管理员电子邮箱地址是否仍然正确。具体如下图所示&#xff1a; 如果点击“稍后提醒我”&#xff0c;那么管理员邮件验证页面就会在3天后重新显示。 说实话&…...

三、MySQL实例初始化、设置、服务启动关闭、环境变量配置、客户端登入(一篇足以从白走到黑)

目录 1、选择安装的电脑类型、设置端口号 2、选择mysql账号密码加密规则 3、设置root账户密码 4、设置mysql服务名和服务启动策略 5、执行设置&#xff08;初始化mysql实例&#xff09; 6、完成设置 7、MySQL数据库服务的启动和停止 方式一&#xff1a;图形化方式 方式…...

Ubuntu20.04-剪贴板

针对图形界面用户 1.两种方式 1.1 安装Parcellite 简单轻量级剪贴板管理器 sudo apt install parcellite 1.2 安装Gpaste 更强大的剪贴板管理器&#xff0c;包含历史记录和同步功能 sudo apt install gpaste...

springmvc常用的组件

SpringMVC常用组件 以下的Handler也叫Controller。 1、DispatcherServlet&#xff1a;前端控制器&#xff0c;不需要工程师开发&#xff0c;由框架提供 作用&#xff1a;统一处理请求和响应&#xff0c;整个流程控制的中心&#xff0c;由它调用其它组件处理用户的请求 2、H…...

Mysql中设置只允许指定ip能连接访问(可视化工具的方式)

场景 Mysql中怎样设置指定ip远程访问连接&#xff1a; Mysql中怎样设置指定ip远程访问连接_navicat for mysql 设置只有某个ip可以远程链接-CSDN博客 前面设置root账户指定ip能连接访问是通过命令行的方式&#xff0c;如果通过可视化工具比如Navicat来实现。 注&#xff1a…...

vue2+webpack升级vue3+vite,报错Cannot read properties of null (reading ‘isCE‘)

同学们可以私信我加入学习群&#xff01; 正文开始 前言问题分析解决总结 前言 系列文章&#xff1a;vue2webpack升级vue3vite&#xff0c;修改插件兼容性bug 前面的文章主要是介绍&#xff0c;在升级初始阶段遇到的一些显而易见的兼容性问题和bug。随着项目迭代的不断深入&a…...

【性能调优】local模式下flink处理离线任务能力分析

文章目录 一. flink的内存管理1.Jobmanager的内存模型2.TaskManager的内存模型2.1. 模型说明2.2. 通讯、数据传输方面2.3. 框架、任务堆外内存2.4. 托管内存 3.任务分析 二. 单个节点的带宽瓶颈1. 带宽相关理论2. 使用speedtest-cli 测试带宽3. 任务分析3. 其他工具使用介绍 本…...

Zabbix监控(2)

目录 一.自动发现 配置自动发现&#xff1a;&#xff08;被动模式&#xff09; 修改三台服务器的hosts文件&#xff1a; 修改agent02的配置文件&#xff1a; 访问页面&#xff0c;删除客服端主机配置&#xff1a; 在配置的自动发现中添加规则&#xff1a; 我们重启的zab…...

uni-app中代理的两种配置方式

方式一: 在项目的 manifest.json 文件中点击 源码视图 在最底部的vue版本下编写代理代码 方式二: 在项目中创建 vue.config.js 文件然后进行配置 在页面中发起请求 完整的url&#xff1a;http://c.m.163.com/recommend/getChanListNews?channelT1457068979049&size10 …...

循环异步调取接口使用数组promiseList保存,Promise.all(promiseList)获取不到数组内容,then()返回空数组

在使用 vue vant2.13.2 技术栈的项目中&#xff0c;因为上传文件的接口是单文件上传&#xff0c;当使用批量上传时&#xff0c;只能循环调取接口&#xff1b;然后有校验内容&#xff1a;需要所有文件上传成功后才能保存&#xff0c;在文件上传不成功时点击保存按钮&#xff0c…...

C++轮子 · STL 序列容器

STL中大家最耳熟能详的可能就是容器,容器大致可以分为两类,序列型容器(SequenceContainer)和关联型容器(AssociativeContainer)这篇文章中将会重点介绍STL中的各种序列型容器和相关的容器适配器。主要内容包括 std::vectorstd::arraystd::dequestd::queuestd::stackstd::…...

浅谈智慧路灯安全智能供电方案设计

摘要: 智慧路灯&#xff0c;作为智慧城市、新基建、城市更新的主要组成部分&#xff0c;近些年在各大城市已得到很好的落地和 应用&#xff0c;但其与传统路灯相比集成大量异元异构电子设备&#xff0c;这些设备的供电电压、接口形式、权属单位各不相同&#xff0c; 如何设计一…...

C#设计模式教程(2):工厂方法模式

工厂方法模式是一种创建型设计模式,它定义了一个用于创建对象的接口,但让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。 C# 代码实现 以下是C#中实现工厂方法模式的一个简单示例: 首先,定义一个抽象产品(Product)类,它是所有具体产品的基类。 pu…...

程序员的能力-如何成为不会过时的“码农”

码农是指从事编程工作的人&#xff0c;也被称为程序员或开发者。他们使用计算机语言和工具来编写、测试和维护软件程序或网站。码农通常需要具备扎实的计算机科学知识、编程技能和问题解决能力&#xff0c;以及良好的逻辑思维和团队合作能力。他们可以在软件开发公司、科技企业…...

【OpenAI】自定义GPTs应用(GPT助手应用)及外部API接口请求

11月10日&#xff0c;OpenAI正式宣布向所有ChatGPT Plus用户开放GPTs功能 简而言之&#xff1a;GPT应用市场(简称GPTs, 全称GPT Store) Ps&#xff1a; 上图为首次进入时的页面&#xff0c;第一部分是自己创建的GPTs应用&#xff0c;下面是公开可以使用的GPTs应用 一、创建GPTs…...

canvas绘制不同样式的五角星(图文示例)

查看专栏目录 canvas实例应用100专栏&#xff0c;提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重…...

C#: BitConverter 字节数组byte[ ] 转各种数据类型用法列举

说明&#xff1a;C# BitConverter 字节数组byte[ ] 转各种数据类型用法示例 1.ToBoolean(byte[] value, int startIndex)&#xff1a;将指定字节数组中从指定索引开始的两个字节转换为布尔值。 byte[] bytes { 1, 0 }; bool result BitConverter.ToBoolean(bytes, 0); // 输…...

CTF show Web 红包题第六弹

提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框&#xff0c;很难让人不联想到SQL注入&#xff0c;但提示都说了不是SQL注入&#xff0c;所以就不往这方面想了 ​ 先查看一下网页源码&#xff0c;发现一段JavaScript代码&#xff0c;有一个关键类ctfs…...

通过Wrangler CLI在worker中创建数据库和表

官方使用文档&#xff1a;Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后&#xff0c;会在本地和远程创建数据库&#xff1a; npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库&#xff1a; 现在&#xff0c;您的Cloudfla…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换

目录 关键点 技术实现1 技术实现2 摘要&#xff1a; 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式&#xff08;自动驾驶、人工驾驶、远程驾驶、主动安全&#xff09;&#xff0c;并通过实时消息推送更新车…...

uniapp 实现腾讯云IM群文件上传下载功能

UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中&#xff0c;群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS&#xff0c;在uniapp中实现&#xff1a; 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...

数据结构:递归的种类(Types of Recursion)

目录 尾递归&#xff08;Tail Recursion&#xff09; 什么是 Loop&#xff08;循环&#xff09;&#xff1f; 复杂度分析 头递归&#xff08;Head Recursion&#xff09; 树形递归&#xff08;Tree Recursion&#xff09; 线性递归&#xff08;Linear Recursion&#xff09;…...

LangChain 中的文档加载器(Loader)与文本切分器(Splitter)详解《二》

&#x1f9e0; LangChain 中 TextSplitter 的使用详解&#xff1a;从基础到进阶&#xff08;附代码&#xff09; 一、前言 在处理大规模文本数据时&#xff0c;特别是在构建知识库或进行大模型训练与推理时&#xff0c;文本切分&#xff08;Text Splitting&#xff09; 是一个…...