贪吃蛇项目实战解析

项目实战

  1. 游戏背景​

贪吃蛇是久负盛名的游戏,它也和俄罗斯方块,扫雷等游戏位列经典游戏的行列。
在编程语言的教学中,我们以贪吃蛇为例,从设计到代码实现来提升学生的编程能力和逻辑能力。

目录:

  1. 游戏背景

  2. 游戏效果演示

  3. 目标

  4. 定位

  5. 技术要点

  6. 贪吃蛇游戏设计与分析

  7. 贪吃蛇游戏数据结构设计

  8. 相关Win32API介绍

  9. 参考代码

正文开始

  1. 游戏背景

贪吃蛇是久负盛名的游戏,它也和俄罗斯方块,扫雷等游戏位列经典游戏的行列。
在编程语言的教学中,我们以贪吃蛇为例,从设计到代码实现来提升编程能力和逻辑能力。
2. 游戏效果演示

  1. 目标

使用C语言在Windows环境的控制台中模拟实现经典小游戏贪吃蛇。

实现基本的功能:
• 贪吃蛇地图绘制
• 蛇吃食物的功能 (上、下、左、右方向键控制蛇的动作)
• 蛇撞墙死亡
• 蛇撞自身死亡
• 计算得分
• 蛇身加速、减速
• 暂停游戏

  1. 定位
    • 提高对编程的兴趣
    • 对C语言语法做一个基本的巩固。
    • 对游戏开发有兴趣的同学做一个启发。
    • 项目适合:C语言学完的同学,有一定的代码能力,初步接触数据结构中的链表。

  2. 技术要点

C语言函数、枚举、结构体、动态内存管理、预处理指令、链表、Win32 API等。

  1. Win32 API介绍

本次实现贪吃蛇会使用到的一些Win32 API知识,接下来我们就学习一下。

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

6.2 控制台程序
平常我们运行起来的黑框程序其实就是控制台程序

我们可以使用cmd命令来设置控制台窗口的长宽:设置控制台窗口的大小,30行,100列

1 mode con cols=100 lines=30

参考:mode命令

也可以通过命令设置控制台窗口的名字:

1 title 贪吃蛇      

参考:title命令

这些能在控制台窗口执行的命令,也可以调用C语言函数system来执行。例如:

1 #include <stdio.h>
2 int main()
3 {4      //设置控制台窗口的长宽:设置控制台窗口的大小,30行,100列5      system("mode con cols=100 lines=30");6      //设置cmd窗口名称7      system("title 贪吃蛇"); 8      return 0;9  }

6.3 控制台屏幕上的坐标COORD
COORD 是Windows API中定义的一个结构体,表示一个字符在控制台屏幕幕缓冲区上的坐标,坐标系
(0,0) 的原点位于缓冲区的顶部左侧单元格。

COORD类型的声明:1  typedef struct _COORD {2      SHORT X;3      SHORT Y;4  } COORD, *PCOORD;

给坐标赋值:

 1  COORD pos = { 10, 15 };

6.4 GetStdHandle
GetStdHandle是一个Windows API函数。它用于从一个特定的标准设备(标准输入、标准输出或标
准错误)中取得一个句柄(用来标识不同设备的数值),使用这个句柄可以操作设备。

 1  HANDLE GetStdHandle(DWORD nStdHandle);

实例:

 1  HANDLE hOutput = NULL;23  //获取标准输出的句柄(用来标识不同设备的数值)4  hOutput = GetStdHandle(STD_OUTPUT_HANDLE);

6.5 GetConsoleCursorInfo
检索有关指定控制台屏幕缓冲区的光标大小和可见性的信息

 1  BOOL WINAPI GetConsoleCursorInfo(2      HANDLE               hConsoleOutput,3      PCONSOLE_CURSOR_INFO lpConsoleCursorInfo4  );                   56  PCONSOLE_CURSOR_INFO  是指向 CONSOLE_CURSOR_INFO 结构的指针,该结构接收有关主机游标(光

实例:

 1  HANDLE hOutput = NULL;2  //获取标准输出的句柄(用来标识不同设备的数值)3  hOutput = GetStdHandle(STD_OUTPUT_HANDLE);45  CONSOLE_CURSOR_INFO CursorInfo;6  GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息

6.5.1 CONSOLE_CURSOR_INFO

这个结构体,包含有关控制台光标的信息

 1  typedef struct _CONSOLE_CURSOR_INFO {2    DWORD dwSize;3    BOOL  bVisible;4  } CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;

• dwSize,由光标填充的字符单元格的百分比。 此值介于1到100之间。 光标外观会变化,范围从完
全填充单元格到单元底部的水平线条。
• bVisible,游标的可见性。 如果光标可见,则此成员为 TRUE。

 1  CursorInfo.bVisible = false; //隐藏控制台光标

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

 1  BOOL WINAPI SetConsoleCursorInfo(2      HANDLE  hConsoleOutput,3      const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo4  );                  

实例:


```cpp1  HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);23  //影藏光标操作4  CONSOLE_CURSOR_INFO CursorInfo;5  GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息6  CursorInfo.bVisible = false; //隐藏控制台光标7  SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态
6.7                                        SetConsoleCursorPosition
设置指定控制台屏幕缓冲区中的光标位置,我们将想要设置的坐标信息放在COORD类型的pos中,调
用SetConsoleCursorPosition函数将光标位置设置到指定的位置。```cpp1  BOOL WINAPI SetConsoleCursorPosition(2      HANDLE hConsoleOutput,3      COORD  pos4  );

实例:


```cpp1      COORD pos = { 10, 5};2      HANDLE hOutput = NULL;3      //获取标准输出的句柄(用来标识不同设备的数值)4      hOutput = GetStdHandle(STD_OUTPUT_HANDLE);5      //设置标准输出上光标的位置为pos6      SetConsoleCursorPosition(hOutput, pos);
SetPos:封装一个设置光标位置的函数```cpp
1  //设置光标的坐标2  void SetPos(short x, short y)3  {4      COORD pos = { x, y };5      HANDLE hOutput = NULL;6      //获取标准输出的句柄(用来标识不同设备的数值)7      hOutput = GetStdHandle(STD_OUTPUT_HANDLE);8      //设置标准输出上光标的位置为pos9      SetConsoleCursorPosition(hOutput, pos);
10  }

6.8 GetAsyncKeyState
获取按键情况,GetAsyncKeyState的函数原型如下:

 1  SHORT GetAsyncKeyState(2      int vKey3  );

将键盘上每个键的虚拟键值传递给函数,函数通过返回值来分辨按键的状态。

GetAsyncKeyState 的返回值是short类型,在上一次调用
GetAsyncKeyState 函数后,
如果返回的16位的short数据中,最高位是1,说明按键的状态是按下,如果最高是0,说明按键的状态是抬
起;如果最低位被置为1则说明,该按键被按过,否则为0。

如果我们要判断一个键是否被按过,可以检测GetAsyncKeyState返回值的最低值是否为1.

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

实例:检测数字键

 1  #include <stdio.h>2  #include <windows.h>34  int main()5  { 6      while (1)7      {8          if (KEY_PRESS(0x30))9          {
10              printf("0\n");
11          }
12          else if (KEY_PRESS(0x31))
13          {
14              printf("1\n");
15          }
16          else if (KEY_PRESS(0x32))
17          {
18              printf("2\n");
19          }
20          else if (KEY_PRESS(0x33))
21          {
22              printf("3\n");
23          }
24          else if (KEY_PRESS(0x34))
25          {
26              printf("4\n");
27          }
28          else if (KEY_PRESS(0x35))
29          {
30              printf("5\n");
31          }
32          else if (KEY_PRESS(0x36))33          {
34              printf("6\n");
35          }
36          else if (KEY_PRESS(0x37))
37          {
38              printf("7\n");
39          }
40          else if (KEY_PRESS(0x38))
41          {
42              printf("8\n");
43          }
44          else if (KEY_PRESS(0x39))
45          {
46              printf("9\n");
47          }
48      }
49      return 0;
50  }
  1. 贪吃蛇游戏设计与分析

7.1 地图

核心设计架构
在这里插入图片描述

这里不得不讲一下控制台窗口的一些知识,如果想在控制台的窗口中指定位置输出信息,我们得知道
该位置的坐标,所以首先介绍一下控制台窗口的坐标知识。

控制台窗口的坐标如下所示,横向的是X轴,从左向右依次增长,纵向是Y轴,从上到下依次增长。

在游戏地图上,我们打印墙体使用宽字符:□,打印蛇使用宽字符●,打印食物使用宽字符★

普通的字符是占一个字节的,这类宽字符是占用2个字节。

这里再简单的讲一下C语言的国际化特性相关的知识,过去C语言并不适合非英语国家(地区)使用。
C语言最初假定字符都是单字节的。但是这些假定并不是在世界的任何地方都适用。

C语言字符默认是采用ASCII编码的,ASCII字符集采用的是单字节编码,且只使用了单字节中的低7
位,最高位是没有使用的,可表示为0xxxxxxxx;可以看到,ASCII字符集共包含128个字符,在英语
国家中,128个字符是基本够用的,但是,在其他国家语言中,比如,在法语中,字母上方有注音符 号,它就无法用 ASCII
码表示。于是,一些欧洲国家就决定,利用字节中闲置的最高位编入新的符
号。比如,法语中的é的编码为130(二进制10000010)。这样一来,这些欧洲国家使用的编码体
系,可以表示最多256个符号。但是,这里又出现了新的问题。不同的国家有不同的字母,因此,哪
怕它们都使用256个符号的编码方式,代表的字母却不一样。比如,130在法语编码中代表了é,在希
在俄语编码中又会代表另一个符号。但是不管怎样,所有这,(ג) 伯来语编码中却代表了字母Gimel
些编码方式中,0–127表示的符号是一样的,不一样的只是128–255的这一段。

至于亚洲国家的文字,使用的符号就更多了,汉字就多达10万左右。一个字节只能表示256种符号,
肯定是不够的,就必须使用多个字节表达一个符号。比如,简体中文常见的编码方式是 GB2312,使
用两个字节表示一个汉字,所以理论上最多可以表示 256 x 256 = 65536 个符号。

后来为了使C语言适应国际化,C语言的标准中不断加入了国际化的支持。比如:加入了宽字符的类型 wchar_t
和宽字符的输入和输出函数,加入了<locale.h>头文件,其中提供了允许程序员针对特定
地区(通常是国家或者说某种特定语言的地理区域)调整程序行为的函数。

7.1.1 <locale.h>本地化 <locale.h>提供的函数用于控制C标准库中对于不同的地区会产生不一样行为的部分。

在标准中,依赖地区的部分有以下几项: • 数字量的格式 • 货币量的格式 • 字符集 • 日期和时间的表示形式

7.1.2 类项 通过修改地区,程序可以改变它的行为来适应世界的不同区域。但地区的改变可能会影响库的许多部 分,其中一部分可能是我们不希望修改的。所以C语言支持针对不同的类项进行修改,下面的一个宏, 指定一个类项:

• LC_COLLATE:影响字符串比较函数 strcoll() 和 strxfrm() 。 •
LC_CTYPE:影响字符处理函数的行为。 • LC_MONETARY:影响货币格式。 • LC_NUMERIC:影响
printf() 的数字格式。 • LC_TIME:影响时间格式 strftime() 和 wcsftime() 。 •
LC_ALL - 针对所有类项修改,将以上所有类别设置为给定的语言环境。 每个类项的详细说明

7.1.3 setlocale函数


```cpp1 char* setlocale (int category, const char* locale);

setlocale 函数用于修改当前地区,可以针对一个类项修改,也可以针对所有类项。

setlocale 的第一个参数可以是前面说明的类项中的一个,那么每次只会影响一个类项,如果第一个参
数是LC_ALL,就会影响所有的类项。

C标准给第二个参数仅定义了2种可能取值:“C”(正常模式)和" "(本地模式)。

在任意程序执行开始,都会隐藏式执行调用:


```cpp1 setlocale(LC_ALL, "C");

当地区设置为"C"时,库函数按正常方式执行,小数点是一个点。

当程序运行起来后想改变地区,就只能显示调用setlocale函数。用" "作为第2个参数,调用setlocale
函数就可以切换到本地模式,这种模式下程序会适应本地环境。

比如:切换到我们的本地模式后就支持宽字符(汉字)的输出等。

   1  setlocale(LC_ALL, " ");//切换到本地环境

7.1.4 宽字符的打印
那如果想在屏幕上打印宽字符,怎么打印呢?

宽字符的字面量必须加上前缀“L”,否则 C 语言会把字面量当作窄字符类型处理。前缀“L”在单引
号前面,表示宽字符,对应 wprintf() 的占位符为 %lc ;在双引号前面,表示宽字符串,对应
wprintf() 的占位符为 %ls 。

 1  #include <stdio.h>2  #include<locale.h>34  int main() {5          setlocale(LC_ALL, "");6          wchar_t ch1 = L'●';7          wchar_t ch2 = L'比';8          wchar_t ch3 = L'特';9          wchar_t ch4 = L'★';
10          
11          printf("%c%c\n", 'a', 'b');
12          
13          wprintf(L"%lc\n", ch1);
14          wprintf(L"%lc\n", ch2);
15          wprintf(L"%lc\n", ch3);
16          wprintf(L"%lc\n", ch4);
17          return 0;
18  }

输出结果:在这里插入图片描述

                                   从输出的结果来看,我们发现一个普通字符占一个字符的位置但是打印一个汉字字符,占用2个字符的位置,那么我们如果要在贪吃蛇中使用宽字符,就得处理好地图上坐标的计算。

普通字符和宽字符打印出宽度的展示如下:

7.1.5 地图坐标
我们假设实现一个棋盘27行,58列的棋盘(行和列可以根据自己的情况修改),再围绕地图画出墙,
如下:

7.2 蛇身和食物
初始化状态,假设蛇的长度是5,蛇身的每个节点是●,在固定的一个坐标处,比如(24, 5)处开始出现
蛇,连续5个节点。

注意:蛇的每个节点的x坐标必须是2个倍数,否则可能会出现蛇的一个节点有一半儿出现在墙体中,
另外一般在墙外的现象,坐标不好对齐。

关于食物,就是在墙体内随机生成一个坐标(x坐标必须是2的倍数),坐标不能和蛇的身体重合,然
后打印★。

7.3 数据结构设计
在游戏运行的过程中,蛇每次吃一个食物,蛇的身体就会变长一节,如果我们使用链表存储蛇的信
息,那么蛇的每一节其实就是链表的每个节点。每个节点只要记录好蛇身节点在地图上的坐标就行,
所以蛇节点结构如下:

  1  typedef struct SnakeNode2  {3      int x;4      int y;5      struct SnakeNode* next;6  }SnakeNode, * pSnakeNo管理蛇1  typedef struct Snake2  {3      pSnakeNode _pSnake;//维护整条蛇的指针4      pSnakeNode _pFood;//维护食物的指针5      enum DIRECTION _Dir;//蛇头的方向,默认是向右6      enum GAME_STATUS _Status;//游戏状态7      int _Socre;//游戏当前获得分数8      int _foodWeight;//默认每个食物10分9      int _SleepTime;//每走一步休眠时间10  }Snake, * pSnake;

蛇的方向,可以一一列举,使用枚举

  1  //方向2  enum DIRECTION3  {4      UP = 1,5      DOWN,6      LEFT,7      RIGHT8  };

游戏状态,可以一一列举,使用枚举

 1  //游戏状态               2  enum GAME_STATUS3  {4      OK,//正常运行5      KILL_BY_WALL,//撞墙6      KILL_BY_SELF,//咬到自己7      END_NOMAL//正常结束8  };

7.4

游戏流程设计

  1. 核心逻辑实现分析

8.1 游戏主逻辑
程序开始就设置程序支持本地模式,然后进入游戏的主逻辑。

主逻辑分为3个过程:
• 游戏开始(GameStart)完成游戏的初始化
• 游戏运行(GameRun)完成游戏运行逻辑的实现
• 游戏结束(GameEnd)完成游戏结束的说明,实现资源释放

  1   #include <locale.h>             23   void test()4   {5       int ch = 0;6       srand((unsigned int)time(NULL));78       do9       {10            Snake snake = { 0 };11            GameStart(&snake);12            GameRun(&snake);13            GameEnd(&snake);14            SetPos(20, 15);15            printf("再来一局吗?(Y/N):");16            ch = getchar();17          getchar();//清理\n
18
19      } while (ch == 'Y');
20      SetPos(0, 27);
21  }
22
23  int main()
24  {
25      //修改当前地区为本地模式,为了支持中文宽字符的打印
26      setlocale(LC_ALL, "");
27      //测试逻辑
28      test();
29      return 0;
30  }

8.2 游戏开始(GameStart)
这个模块完成游戏的初始化任务:
• 控制台窗口大小的设置
• 控制台窗口名字的设置
• 鼠标光标的隐藏
• 打印欢迎界面
• 创建地图
• 初始化第蛇
• 创建第一个食物

  1  void GameStart(pSnake ps)2  {3      //设置控制台窗口的大小,30行,100列4      //mode 为DOS命令5      system("mode con cols=100 lines=30");6      //设置cmd窗口名称7      system("title 贪吃蛇"); 89      //获取标准输出的句柄(用来标识不同设备的数值)10      HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);1112      //影藏光标操作13      CONSOLE_CURSOR_INFO CursorInfo;14      GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息15      CursorInfo.bVisible = false; //隐藏控制台光标16      SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态1718      //打印欢迎界面19      WelcomeToGame();20      //打印地图21      CreateMap();22      //初始化蛇23      InitSnake(ps);24      //创造第一个食物25      CreateFood(ps);26  }

8.2.1 打印欢迎界面
在游戏正式开始之前,做一些功能提醒

 1  void WelcomeToGame()2  {3      SetPos(40, 15);4      printf("欢迎来到贪吃蛇小游戏");5      SetPos(40, 25);//让按任意键继续的出现的位置好看点6      system("pause");7      system("cls");8      SetPos(25, 12);9      printf("用 ↑ . ↓ . ← . → 分别控制蛇的移动, F3为加速,F4为减速\n");
10      SetPos(25, 13);
11      printf("加速将能得到更高的分数。\n");
12      SetPos(40, 25);//让按任意键继续的出现的位置好看点
13      system("pause");
14      system("cls");
15  }                   

8.2.2 创建地图
创建地图就是将墙打印出来,因为是宽字符打印,所有使用wprintf函数,打印格式串前使用L

打印地图的关键是要算好坐标,才能在想要的位置打印墙体。

墙体打印的宽字符:

1  #define WALL L'□'

易错点:就是坐标的计算

上:(0,0)到(56,0)

下:(0,26)到(56,26)

左:(0,1)到(0,25)

右:(56,1)到(56,25)

创建地图函数CreateMap

 1  void CreateMap()2  {3      int i = 0;4      //上(0,0)-(56, 0)5      SetPos(0, 0);6      for (i = 0; i < 58; i += 2)7      {8          wprintf(L"%c", WALL);9      }
10      //下(0,26)-(56, 26)
11      SetPos(0, 26);
12      for (i = 0; i < 58; i += 2)
13      {
14          wprintf(L"%c", WALL);
15      }
16      //左
17      //x是0,y从1开始增长
18      for (i = 1; i < 26; i++)
19      {
20          SetPos(0, i);
21          wprintf(L"%c", WALL);
22      }
23      //x是56,y从1开始增长
24      for (i = 1; i < 26; i++)
25      {
26          SetPos(56, i);
27          wprintf(L"%c", WALL);
28      }29  }                           

8.2.3 初始化蛇身
蛇最开始长度为5节,每节对应链表的一个节点,蛇身的每一个节点都有自己的坐标。

创建5个节点,然后将每个节点存放在链表中进行管理。创建完蛇身后,将蛇的每一节打印在屏幕上。
• 蛇的初始位置从 (24,5) 开始。
再设置当前游戏的状态,蛇移动的速度,默认的方向,初始成绩,每个食物的分数。
• 游戏状态是:OK
• 蛇的移动速度:200毫秒
• 蛇的默认方向:RIGHT
• 初始成绩:0
• 每个食物的分数:10

蛇身打印的宽字符:

1 #define BODY L'●' 

初始化蛇身函数:InitSnake

1  void InitSnake(pSnake ps)2  {3      pSnakeNode cur = NULL;4      int i = 0;5      //创建蛇身节点,并初始化坐标6      //头插法7      for (i = 0; i < 5; i++)8      {9          //创建蛇身的节点
10          cur = (pSnakeNode)malloc(sizeof(SnakeNode));
11          if (cur == NULL)
12          {
13              perror("InitSnake()::malloc()");
14              return;
15          }
16          //设置坐标
17          cur->next = NULL;
18          cur->x = POS_X + i * 2;
19          cur->y = POS_Y;
20
21          //头插法
22          if (ps->_pSnake == NULL)
23          {
24              ps->_pSnake = cur;
25          }
26          else
27          {
28              cur->next = ps->_pSnake;
29              ps->_pSnake = cur;
30          }
31      }                
32
33      //打印蛇的身体
34      cur = ps->_pSnake;
35      while (cur)
36      {
37          SetPos(cur->x, cur->y);
38          wprintf(L"%lc", BODY);
39          cur = cur->next;
40      }
41
42      //初始化贪吃蛇数据
43      ps->_SleepTime = 200;
44      ps->_Socre = 0;
45      ps->_Status = OK;
46      ps->_Dir = RIGHT;47     ps->_foodWeight = 10;48 }

8.2.4 创建第一个食物
• 先随机生成食物的坐标
◦ x坐标必须是2的倍数
◦ 食物的坐标不能和蛇身每个节点的坐标重复
• 创建食物节点,打印食物

食物打印的宽字符:

1 #define FOOD L'★'

创建食物的函数:CreateFood

    1 void CreateFood(pSnake ps)2 {3     int x = 0;4      int y = 0;56  again:7      //产生的x坐标应该是2的倍数,这样才可能和蛇头坐标对齐。8      do9      {
10          x = rand() % 53 + 2;
11          y = rand() % 25 + 1;
12      } while (x % 2 != 0);
13
14      pSnakeNode cur = ps->_pSnake;//获取指向蛇头的指针
15      //食物不能和蛇身冲突
16      while (cur)
17      {
18          if (cur->x == x && cur->y == y)
19          {
20              goto again;
21          }
22          cur = cur->next;
23      }
24
25      pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode)); //创建食物
26      if (pFood == NULL)
27      {
28          perror("CreateFood::malloc()");
29          return;
30      }
31      else
32      {
33          pFood->x = x;
34          pFood->y = y;
35          SetPos(pFood->x, pFood->y);
36          wprintf(L"%c", FOOD);
37          ps->_pFood = pFood;
38      }
39  }

8.3 游戏运行(GameRun)
游戏运行期间,右侧打印帮助信息,提示玩家,坐标开始位置(64, 15)

根据游戏状态检查游戏是否继续,如果是状态是OK,游戏继续,否则游戏结束。

如果游戏继续,就是检测按键情况,确定蛇下一步的方向,或者是否加速减速,是否暂停或者退出游
戏。

需要的虚拟按键的罗列:
• 上:VK_UP
• 下:VK_DOWN
• 左:VK_LEFT
• 右:VK_RIGHT
• 空格:VK_SPACE
• ESC:VK_ESCAPE
• F3:VK_F3
• F4:VK_F4

确定了蛇的方向和速度,蛇就可以移动了。

 1  void GameRun(pSnake ps)2  {3      //打印右侧帮助信息4      PrintHelpInfo();5      do6      {7          SetPos(64, 10);8          printf("得分:%d ", ps->_Socre);9          printf("每个食物得分:%d分", ps->_foodWeight);
10          if (KEY_PRESS(VK_UP) && ps->_Dir != DOWN)
11          {
12              ps->_Dir = UP;
13          }
14          else if (KEY_PRESS(VK_DOWN) && ps->_Dir != UP)
15          {
16              ps->_Dir = DOWN;
17          }
18          else if (KEY_PRESS(VK_LEFT) && ps->_Dir != RIGHT)
19          {
20              ps->_Dir = LEFT;
21          }
22          else if (KEY_PRESS(VK_RIGHT) && ps->_Dir != LEFT)
23          {
24              ps->_Dir = RIGHT;
25          }
26          else if (KEY_PRESS(VK_SPACE))
27          {
28              pause();
29          }
30          else if (KEY_PRESS(VK_ESCAPE))
31          {
32              ps->_Status = END_NOMAL;
33              break;
34          }
35          else if (KEY_PRESS(VK_F3))
36          {
37              if (ps->_SleepTime >= 80)
38              {
39                  ps->_SleepTime -= 30;
40                  ps->_foodWeight += 2;//一个食物分数最高是20分
41              }
42          }
43          else if (KEY_PRESS(VK_F4))
44          {
45              if (ps->_SleepTime < 320)
46              {
47                  ps->_SleepTime += 30;48                  ps->_foodWeight -= 2;//一个食物分数最低是2分49              }50          }51          //蛇每次一定之间要休眠的时间,时间短,蛇移动速度就快52          Sleep(ps->_SleepTime);53          SnakeMove(ps);5455      } while (ps->_Status == OK);56  }

8.3.1 KEY_PRESS
检测按键状态,我们封装了一个宏

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

8.3.2 PrintHelpInfo

 1  void PrintHelpInfo()2  {3      //打印提示信息4      SetPos(64, 15);5      printf("不能穿墙,不能咬到自己\n");6      SetPos(64, 16);7      printf("用↑.↓.←.→分别控制蛇的移动.");8      SetPos(64, 17);9      printf("F3 为加速,F4 为减速\n");
10      SetPos(64, 18);
11      printf("ESC :退出游戏.space:暂停游戏.");
12      SetPos(64, 20);
13      printf("加油");
14  }

8.3.3 蛇身移动(SnakeMove)
先创建下一个节点,根据移动方向和蛇头的坐标,蛇移动到下一个位置的坐标。

确定了下一个位置后,看下一个位置是否是食物(NextIsFood),是食物就做吃食物处理
(EatFood),如果不是食物则做前进一步的处理(NoFood)。

蛇身移动后,判断此次移动是否会造成撞墙(KillByWall)或者撞上自己蛇身(KillBySelf),从而影
响游戏的状态。

1  void SnakeMove(pSnake ps)2  {3      //创建下一个节点4      pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));5      if (pNextNode == NULL)6      {7          perror("SnakeMove()::malloc()");8          return;9      }
10      //确定下一个节点的坐标,下一个节点的坐标根据,蛇头的坐标和方向确定
11      switch (ps->_Dir)
12      {
13          case UP:
14          {
15              pNextNode->x = ps->_pSnake->x;
16              pNextNode->y = ps->_pSnake->y - 1;17          }
18          break;
19          case DOWN:
20          {
21              pNextNode->x = ps->_pSnake->x;
22              pNextNode->y = ps->_pSnake->y + 1;
23          }
24          break;
25          case LEFT:
26          {
27              pNextNode->x = ps->_pSnake->x - 2;
28              pNextNode->y = ps->_pSnake->y;
29          }
30          break;
31          case RIGHT:
32          {
33              pNextNode->x = ps->_pSnake->x + 2;
34              pNextNode->y = ps->_pSnake->y;
35          }
36          break;
37      }
38
39      //如果下一个位置就是食物
40      if (NextIsFood(pNextNode, ps))
41      {
42          EatFood(pNextNode, ps);
43      }
44      else//如果没有食物
45      {
46          NoFood(pNextNode, ps);
47      }
48                       
49      KillByWall(ps);
50      KillBySelf(ps);
51  }
 1  //pSnakeNode psn 是下一个节点的地址2  //pSnake ps 维护蛇的指针3  int NextIsFood(pSnakeNode psn, pSnake ps)4  {5      return (psn->x == ps->_pFood->x) && (psn->y == ps->_pFood->y);6  }1  //pSnakeNode psn 是下一个节点的地址2  //pSnake ps 维护蛇的指针3  void EatFood(pSnakeNode psn, pSnake ps)4  {5      //头插法6      psn->next = ps->_pSnake;7      ps->_pSnake = psn;8      9      //打印蛇
10      pSnakeNode cur = ps->_pSnake;
11      while (cur)
12      {
13          SetPos(cur->x, cur->y);
14          wprintf(L"%c", BODY);
15          cur = cur->next;
16      }
17      ps->_Socre += ps->_foodWeight;
18      
19      //释放食物节点
20      free(ps->_pFood);
21      //创建新的食物
22      CreateFood(ps);
23  }

8.3.3.3 NoFood
将下一个节点头插入蛇的身体,并将之前蛇身最后一个节点打印为空格,释放掉蛇身的最后一个节
点。
易错点:这里最容易错误的是,释放最后一个结点后,还得将指向在最后一个结点的指针改为NULL,
保证蛇尾打印可以正常结束,不会越界访问。

 1  //pSnakeNode psn 是下一个节点的地址2  //pSnake ps 维护蛇的指针3  void NoFood(pSnakeNode psn, pSnake ps)4  {5      //头插法6      psn->next = ps->_pSnake;7      ps->_pSnake = psn;8      9      //打印蛇
10      pSnakeNode cur = ps->_pSnake;11      while (cur->next->next)
12      {
13          SetPos(cur->x, cur->y);
14          wprintf(L"%c", BODY);
15          cur = cur->next;
16      }
17
18      //最后一个位置打印空格,然后释放节点
19      SetPos(cur->next->x, cur->next->y);
20      printf("  ");
21      free(cur->next);
22      cur->next = NULL;
23  }

8.3.3.4 KillByWall
判断蛇头的坐标是否和墙的坐标冲突

  1  //pSnake ps 维护蛇的指针2  int KillByWall(pSnake ps)3  {4      if ((ps->_pSnake->x == 0)5          || (ps->_pSnake->x == 56)6          || (ps->_pSnake->y == 0)7          || (ps->_pSnake->y == 26))8      {9          ps->_Status = KILL_BY_WALL;10          return 1;11      }12      return 0;13  }

8.3.3.5 KillBySelf
判断蛇头的坐标是否和蛇身体的坐标冲突

   1  //pSnake ps 维护蛇的指针2  int KillBySelf(pSnake ps)3  {4      pSnakeNode cur = ps->_pSnake->next;5      while (cur)6      {7          if ((ps->_pSnake->x == cur->x)8              && (ps->_pSnake->y == cur->y))9          {10              ps->_Status = KILL_BY_SELF;11              return 1;12          }13          cur = cur->next;14      }15      return 0;16  }17

8.4 游戏结束
游戏状态不再是OK(游戏继续)的时候,要告知游戏结束的原因,并且释放蛇身节点。

  1  void GameEnd(pSnake ps)2  {3      pSnakeNode cur = ps->_pSnake;4      SetPos(24, 12);5      switch (ps->_Status)6      {7      case END_NOMAL:8          printf("您主动退出游戏\n");9          break;10      case KILL_BY_SELF:11          printf("您撞上自己了 ,游戏结束!\n");12          break;13      case KILL_BY_WALL:14          printf("您撞墙了,游戏结束!\n");15          break;16      }1718      //释放蛇身的节点19      while (cur)20      {21          pSnakeNode del = cur;22          cur = cur->next;23          free(del);24      }25  }
  1. 参考代码

完整代码实现,分3个文件实现

test.cpp1  #include "Snake.h"2  #include <locale.h>34  void test()5  {6      int ch = 0;7      srand((unsigned int)time(NULL));8                       9      do10      {11          Snake snake = { 0 };12          GameStart(&snake);13          GameRun(&snake);14          GameEnd(&snake);15          SetPos(20, 15);16          printf("再来一局吗?(Y/N):");17          ch = getchar();18          getchar();//清理\n1920      } while (ch == 'Y' || ch == 'y');21      SetPos(0, 27);22  }24  int main()25  {26      //修改当前地区为本地模式,为了支持中文宽字符的打印27      setlocale(LC_ALL, "");28      //测试逻辑29      test();30      return 0;31  }snake.h1  #pragma once23  #include <windows.h>4  #include <time.h>5  #include <stdio.h>67  #define KEY_PRESS(VK)  ((GetAsyncKeyState(VK)&0x1) ? 1 : 0)89  //方向10  enum DIRECTION11  {12      UP = 1,13      DOWN,14      LEFT,15      RIGHT16  };                1718  //游戏状态19  enum GAME_STATUS20  {21      OK,//正常运行22      KILL_BY_WALL,//撞墙23      KILL_BY_SELF,//咬到自己24      END_NOMAL//正常结束25  };2627  #define WALL L'□'28  #define BODY L'●'  //★○●◇◆□■29  #define FOOD L'★'  //★○●◇◆□■3031  //蛇的初始位置32  #define POS_X 24
33  #define POS_Y 5
34
35  //蛇身节点
36  typedef struct SnakeNode
37  {
38      int x;
39      int y;
40      struct SnakeNode* next;
41  }SnakeNode, * pSnakeNode;
42
43  typedef struct Snake
44  {
45      pSnakeNode _pSnake;//维护整条蛇的指针
46      pSnakeNode _pFood;//维护食物的指针
47      enum DIRECTION _Dir;//蛇头的方向默认是向右
48      enum GAME_STATUS _Status;//游戏状态
49      int _Socre;//当前获得分数
50      int _foodWeight;//默认每个食物10分
51      int _SleepTime;//每走一步休眠时间
52  }Snake, * pSnake;
53
54
55
56  //游戏开始前的初始化
57  void GameStart(pSnake ps);
58
59  //游戏运行过程
60  void GameRun(pSnake ps);
61
62  //游戏结束
63  void GameEnd(pSnake ps);
64
65  //设置光标的坐标
66  void SetPos(short x, short y);
67
68  //欢迎界面
69  void WelcomeToGame();
70
71  //打印帮助信息
72  void PrintHelpInfo();
73
74  //创建地图
75  void CreateMap();
76
77  //初始化蛇
78  void InitSnake(pSnake ps);80  //创建食物81  void CreateFood(pSnake ps);8283  //暂停响应84  void pause();8586  //下一个节点是食物87  int NextIsFood(pSnakeNode psn, pSnake ps);8889  //吃食物90  void EatFood(pSnakeNode psn, pSnake ps);9192  //不吃食物93  void NoFood(pSnakeNode psn, pSnake ps);9495  //撞墙检测96  int KillByWall(pSnake ps);9798  //撞自身检测99  int KillBySelf(pSnake ps);100101  //蛇的移动102  void SnakeMove(pSnake ps);103104  //游戏初始化105  void GameStart(pSnake ps);106107  //游戏运行108  void GameRun(pSnake ps);109110  //游戏结束             111  void GameEnd(pSnake ps);snake.cpp1  #include "Snake.h"23  //设置光标的坐标4  void SetPos(short x, short y)5  {6      COORD pos = { x, y };7      HANDLE hOutput = NULL;8      //获取标准输出的句柄(用来标识不同设备的数值)9      hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
10      //设置标准输出上光标的位置为pos
11      SetConsoleCursorPosition(hOutput, pos);
12  }
13
14  void WelcomeToGame()
15  {
16      SetPos(40, 15);
17      printf("欢迎来到贪吃蛇小游戏");
18      SetPos(40, 25);//让按任意键继续的出现的位置好看点
19      system("pause");
20      system("cls");
21      SetPos(25, 12);
22      printf("用 ↑ . ↓ . ← . → 分别控制蛇的移动, F3为加速,F4为减速\n");
23      SetPos(25, 13);
24      printf("加速将能得到更高的分数。\n");
25      SetPos(40, 25);//让按任意键继续的出现的位置好看点
26      system("pause");
27      system("cls");
28  }
29
30  void CreateMap()
31  {
32      int i = 0;
33      //上(0,0)-(56, 0)
34      SetPos(0, 0);
35      for (i = 0; i < 58; i += 2)
36      {
37          wprintf(L"%c", WALL);
38      }
39      //下(0,26)-(56, 26)
40      SetPos(0, 26);
41      for (i = 0; i < 58; i += 2)
42      {
43          wprintf(L"%c", WALL);
44      }
45      //左
46      //x是0,y从1开始增长
47      for (i = 1; i < 26; i++)
48      {
49          SetPos(0, i);
50          wprintf(L"%c", WALL);
51      }
52      //x是56,y从1开始增长
53      for (i = 1; i < 26; i++)
54      {55          SetPos(56, i);56          wprintf(L"%c", WALL);57      }58  }596061  void InitSnake(pSnake ps)62  {63      pSnakeNode cur = NULL;64      int i = 0;65      //创建蛇身节点,并初始化坐标66      //头插法67      for (i = 0; i < 5; i++)68      {69          //创建蛇身的节点70          cur = (pSnakeNode)malloc(sizeof(SnakeNode));71          if (cur == NULL)72          {73              perror("InitSnake()::malloc()");74              return;75          }76          //设置坐标77          cur->next = NULL;78          cur->x = POS_X + i * 2;79          cur->y = POS_Y;8081          //头插法82          if (ps->_pSnake == NULL)83          {84              ps->_pSnake = cur;85          }86          else        87          {88              cur->next = ps->_pSnake;89              ps->_pSnake = cur;90          }91      }9293      //打印蛇的身体94      cur = ps->_pSnake;95      while (cur)96      {97          SetPos(cur->x, cur->y);98          wprintf(L"%c", BODY);99          cur = cur->next;
100      }
101102      //初始化贪吃蛇数据
103      ps->_SleepTime = 200;
104      ps->_Socre = 0;
105      ps->_Status = OK;
106      ps->_Dir = RIGHT;
107      ps->_foodWeight = 10;
108  }
109
110
111
112  void CreateFood(pSnake ps)
113  {
114      int x = 0;
115      int y = 0;
116
117  again:
118      //产生的x坐标应该是2的倍数,这样才可能和蛇头坐标对齐。
119      do
120      {
121          x = rand() % 53 + 2;
122          y = rand() % 25 + 1;
123      } while (x % 2 != 0);
124
125      pSnakeNode cur = ps->_pSnake;//获取指向蛇头的指针
126      //食物不能和蛇身冲突
127      while (cur)
128      {
129          if (cur->x == x && cur->y == y)
130          {
131              goto again;
132          }
133          cur = cur->next;
134      }
135
136      pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode)); //创建食物
137      if (pFood == NULL)
138      {
139          perror("CreateFood::malloc()");
140          return;
141      }
142      else
143      {
144          pFood->x = x;
145          pFood->y = y;
146          SetPos(pFood->x, pFood->y);
147          wprintf(L"%c", FOOD);
148          ps->_pFood = pFood;149      }
150  }
151
152  void PrintHelpInfo()
153  {
154      //打印提示信息
155      SetPos(64, 15);
156      printf("不能穿墙,不能咬到自己\n");
157      SetPos(64, 16);
158      printf("用↑.↓.←.→分别控制蛇的移动.");
159      SetPos(64, 17);
160      printf("F3 为加速,F4 为减速\n");
161      SetPos(64, 18);
162      printf("ESC :退出游戏.space:暂停游戏.");
163      SetPos(64, 20);
164      printf("加油");
165  }
166
167  void pause()//暂停
168  {
169      while (1)
170      {
171          Sleep(300);
172          if (KEY_PRESS(VK_SPACE))
173          {
174              break;
175          }
176      }
177  }
178
179  //pSnakeNode psn 是下一个节点的地址
180  //pSnake ps 维护蛇的指
181  int NextIsFood(pSnakeNode psn, pSnake ps)
182  {
183      return (psn->x == ps->_pFood->x) && (psn->y == ps->_pFood->y);
184  }
185
186  //pSnakeNode psn 是下一个节点的地址
187  //pSnake ps 维护蛇的指针
188  void EatFood(pSnakeNode psn, pSnake ps)
189  {
190      //头插法
191      psn->next = ps->_pSnake;
192      ps->_pSnake = psn;
193      pSnakeNode cur = ps->_pSnake;
194      //打印蛇
195      while (cur)
196      {
197          SetPos(cur->x, cur->y);
198          wprintf(L"%c", BODY);
199          cur = cur->next;
200      }
201      ps->_Socre += ps->_foodWeight;
202
203      free(ps->_pFood);
204      CreateFood(ps);
205  }
206
207  //pSnakeNode psn 是下一个节点的地址
208  //pSnake ps 维护蛇的指针
209  void NoFood(pSnakeNode psn, pSnake ps)
210  {
211      //头插法
212      psn->next = ps->_pSnake;
213      ps->_pSnake = psn;
214      pSnakeNode cur = ps->_pSnake;
215      //打印蛇
216      while (cur->next->next)
217      {
218          SetPos(cur->x, cur->y);
219          wprintf(L"%c", BODY);
220          cur = cur->next;
221      }
222
223      //最后一个位置打印空格,然后释放节点
224      SetPos(cur->next->x, cur->next->y);
225      printf("  ");
226      free(cur->next);
227      cur->next = NULL;
228  }
229
230  //pSnake ps 维护蛇的指针
231  int KillByWall(pSnake ps)
232  {
233      if ((ps->_pSnake->x == 0)
234          || (ps->_pSnake->x == 56)
235          || (ps->_pSnake->y == 0)
236          || (ps->_pSnake->y == 26))
237      {
238          ps->_Status = KILL_BY_WALL;
239          return 1;
240      }
241      return 0;
242  }243
244  //pSnake ps 维护蛇的指针
245  int KillBySelf(pSnake ps)
246  {
247      pSnakeNode cur = ps->_pSnake->next;
248      while (cur)
249      {
250          if ((ps->_pSnake->x == cur->x)
251              && (ps->_pSnake->y == cur->y))
252          {
253              ps->_Status = KILL_BY_SELF;
254              return 1;
255          }
256          cur = cur->next;
257      }
258      return 0;
259  }
260
261
262  void SnakeMove(pSnake ps)
263  {
264      //创建下一个节点
265      pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));
266      if (pNextNode == NULL)
267      {
268          perror("SnakeMove()::malloc()");
269          return;
270      }
271      //确定下一个节点的坐标,下一个节点的坐标根据,蛇头的坐标和方向确定
272      switch (ps->_Dir)
273      {
274          case UP:     
275          {
276              pNextNode->x = ps->_pSnake->x;
277              pNextNode->y = ps->_pSnake->y - 1;
278          }
279          break;
280          case DOWN:
281          {
282              pNextNode->x = ps->_pSnake->x;
283              pNextNode->y = ps->_pSnake->y + 1;
284          }
285          break;
286          case LEFT:
287          {
288              pNextNode->x = ps->_pSnake->x - 2;
289              pNextNode->y = ps->_pSnake->y;290          }
291          break;
292          case RIGHT:
293          {
294              pNextNode->x = ps->_pSnake->x + 2;
295              pNextNode->y = ps->_pSnake->y;
296          }
297          break;
298      }
299
300      //如果下一个位置就是食物
301      if (NextIsFood(pNextNode, ps))
302      {
303          EatFood(pNextNode, ps);
304      }
305      else//如果没有食物
306      {
307          NoFood(pNextNode, ps);
308      }
309
310      KillByWall(ps);
311      KillBySelf(ps);
312  }
313
314
315
316  void GameStart(pSnake ps)
317  {
318      //设置控制台窗口的大小,30行,100列
319      //mode 为DOS命令
320      system("mode con cols=100 lines=30");
321      //设置cmd窗口名称322      system("title  贪吃蛇游戏   "); 
323
324      //获取标准输出的句柄(用来标识不同设备的数值)
325      HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
326
327      //影藏光标操作
328      CONSOLE_CURSOR_INFO CursorInfo;
329      GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息
330      CursorInfo.bVisible = false; //隐藏控制台光标
331      SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态
332
333      //打印欢迎界面
334      WelcomeToGame();
335      //打印地图
336      CreateMap();337      //初始化蛇
338      InitSnake(ps);
339      //创造第一个食物
340      CreateFood(ps);
341  }
342
343
344  void GameRun(pSnake ps)
345  {
346      //打印右侧帮助信息
347      PrintHelpInfo();
348      do
349      {
350          SetPos(64, 10);
351          printf("得分:%d ", ps->_Socre);
352          printf("每个食物得分:%d分", ps->_foodWeight);
353          if (KEY_PRESS(VK_UP) && ps->_Dir != DOWN)
354          {
355              ps->_Dir = UP;
356          }
357          else if (KEY_PRESS(VK_DOWN) && ps->_Dir != UP)
358          {
359              ps->_Dir = DOWN;
360          }
361          else if (KEY_PRESS(VK_LEFT) && ps->_Dir != RIGHT)
362          {
363              ps->_Dir = LEFT;
364          }
365          else if (KEY_PRESS(VK_RIGHT) && ps->_Dir != LEFT)
366          {
367              ps->_Dir = RIGHT;
368          }             
369          else if (KEY_PRESS(VK_SPACE))
370          {
371              pause();
372          }
373          else if (KEY_PRESS(VK_ESCAPE))
374          {
375              ps->_Status = END_NOMAL;
376              break;
377          }
378          else if (KEY_PRESS(VK_F3))
379          {
380              if (ps->_SleepTime >= 50)
381              {
382                  ps->_SleepTime -= 30;
383                  ps->_foodWeight += 2;384              }
385          }
386          else if (KEY_PRESS(VK_F4))
387          {
388              if (ps->_SleepTime < 350)
389              {
390                  ps->_SleepTime += 30;
391                  ps->_foodWeight -= 2;
392                  if (ps->_SleepTime == 350)
393                  {
394                      ps->_foodWeight = 1;
395                  }
396              }
397          }
398          //蛇每次一定之间要休眠的时间,时间短,蛇移动速度就快
399          Sleep(ps->_SleepTime);
400          SnakeMove(ps);
401
402      } while (ps->_Status == OK);
403  }
404
405  void GameEnd(pSnake ps)
406  {
407      pSnakeNode cur = ps->_pSnake;
408      SetPos(24, 12);
409      switch (ps->_Status)
410      {
411      case END_NOMAL:
412          printf("您主动退出游戏\n");
413          break;
414      case KILL_BY_SELF:
415          printf("您撞上自己了 ,游戏结束!\n");
416          break;
417      case KILL_BY_WALL:
418          printf("您撞墙了,游戏结束!\n");
419          break;
420      }
421
422      //释放蛇身的节点
423      while (cur)
424      {
425          pSnakeNode del = cur;
426          cur = cur->next;
427          free(del);
428      }
429  }

代码仓库:https://gitee.com/bitpg/snake

参考:汉字字符集编码查询;中文字符集编码:GB2312、BIG5、GBK、GB18030、Unicode
在这里插入图片描述

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

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

相关文章

Java如何用EasyExcel插件对Excel进行数据导入和数据导出

文章目录 一、EasyExcel的示例导入依赖创建实体类数据导入和导出 二、EasyExcel的作用三、EasyExcel的注解 EasyExcel是一个阿里巴巴开源的excel处理框架&#xff0c;它以使用简单、节省内存著称。在解析Excel时&#xff0c;EasyExcel没有将文件数据一次性全部加载到内存中&…

【Flutter】自动生成图片资源索引插件二:FlutterAssetsGenerator

介绍 FlutterAssetsGenerator 插件 &#xff1a;没乱码&#xff0c;生成的图片索引命名是小驼峰 目录 介绍一、安装二、使用 一、安装 1.安装FlutterAssetsGenerator 插件 生成的资源索引类可以修改名字&#xff0c;我这里改成R 2. 根目录下创建assets/images 3. 点击image…

c#+unity基础

序列化&#xff1a; [SerializeField]&#xff0c;点不出来&#xff0c;只能在面板上显示绑定游戏物体 //公有隐藏 特有函数 特有函数&#xff1a;不需要调用&#xff0c;自动执行 Awake最先执行->OnEable 面向对象思想 面向对象思想&#xff1a;分为具体对象和抽象对…

LeetCode in Python 55. Jump Game (跳跃游戏)

跳跃游戏的游戏规则比较简单&#xff0c;若单纯枚举所有的跳法以判断是否能到达最后一个下标需要的时间复杂度为O()&#xff0c;为此&#xff0c;本文采用贪心策略&#xff0c;从最后一个下标开始逆着向前走&#xff0c;若能跳到第一个元素则表明可以完成跳跃游戏&#xff0c;反…

明文scheme拉起此小程序

微信开发文档说明&#xff1a;https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/url-scheme.html 1、开发者无需调用平台接口&#xff0c;在MP平台->设置->隐私与安全->明文Scheme拉起此小程序声明后&#xff0c;可自行根据如下格式拼接app…

Webrtc 信令服务器实现

webrtc建联流程图 由上图可知&#xff0c;所谓的信令服务器其实就是将peer的offer/candidate/answer传给对端而已。这样的话实现方式就有很多种了&#xff0c;目前普遍的方式HTTP/HTTPS&#xff0c;WS/WSS。像webrtc-demo-peerconnection就是实现HTTP这种方式。本文使用WS&…

论文辅助笔记:处理geolife数据

论文笔记&#xff1a;Context-aware multi-head self-attentional neural network model fornext location prediction-CSDN博客 对应命令行里 python preprocessing/geolife.py 20 这一句 1 读取geolife数据 pfs, _ read_geolife(config["raw_geolife"], print_…

Qt gsl库配置踩坑记录

想求解非线性方程组&#xff0c;之前使用拟牛顿法写过相关的matlab代码&#xff0c;这次想移植到C代码&#xff0c;网上说gsl库挺好用的&#xff0c;于是我也想试一下。相关参考&#xff1a; 【C】GSL(GNU Scientific Library) 的安装及在 Visual Studio 2017 中的使用 QT5使用…

深度学习VGG16网络构建(Pytorch代码从零到一精讲,帮助理解网络的参数定义)

&#x1f4da;博客主页&#xff1a;knighthood2001 ✨公众号&#xff1a;认知up吧 &#xff08;目前正在带领大家一起提升认知&#xff0c;感兴趣可以来围观一下&#xff09; &#x1f383;知识星球&#xff1a;【认知up吧|成长|副业】介绍 ❤️感谢大家点赞&#x1f44d;&…

Labview2024安装包(亲测可用)

目录 一、软件简介 二、软件下载 一、软件简介 LabVIEW是一种由美国国家仪器&#xff08;NI&#xff09;公司开发的程序开发环境&#xff0c;它显著区别于其他计算机语言&#xff0c;如C和BASIC。传统的计算机语言是基于文本的语言来产生代码&#xff0c;而LabVIEW则采用图形化…

CERLAB无人机自主框架: 1-环境搭建

前言&#xff1a;更多更新文章详见我的个人博客主页【MGodmonkeyの世界】 描述&#xff1a;欢迎来到CERLAB无人机自主框架&#xff0c;这是一个用于自主无人飞行器 (UAV) 的多功能模块化框架。该框架包括不同的组件 (模拟器&#xff0c;感知&#xff0c;映射&#xff0c;规划和…

Day23_学点儿JSON_定义、数据格式、和XML比较、插件

1 JSON定义 定义&#xff1a;是一种轻量级的数据交换格式 JSON是JavaScript Object Notation缩写 特点&#xff1a; 易于程序员阅读和编写。易于计算机解析和生成。其实是javascript的子集&#xff1a;原生javascript支持JSON <script type"text/javascript">…

B-树 B+树与数据库原理

B树 引入 概念和性质 插入分析 总结 B树 B*树&#xff08;了解&#xff09; 数据库原理 数据库与B树的关系

【深度学习实战(10)】图像推理之预处理

一、预处理流程 在把一张图像送入模型进行推理时&#xff0c;需要先进行预处理&#xff0c;预处理流程包括&#xff1a; &#xff08;1&#xff09;读取图像 &#xff08;2&#xff09;尺寸调整&#xff0c;letter_box&#xff08;不失真&#xff09; &#xff08;3&#xff0…

小红的排列构造(dp优化)

题目描述 小红拿到了一个长度为n的数组a&#xff0c;她希望你构造两个排列p和q&#xff0c;满足对于i∈[1,n],ai∈[1,n]pi或qi二选一。你能帮帮她吗&#xff1f;定义排列是一个长度为n的数组&#xff0c;其中1到n每个元素恰好出现1次。 输入描述:第一行输入一个正整数n&#…

解析OceanBase v4.2 Oracle 语法兼容之 LOCK TABLE

背景 在OceanBase V4.1及之前的版本中&#xff0c;尽管已经为Oracle租户兼容了LOCK TABLE相关的语法&#xff0c;包括单表锁定操作&#xff0c;和WAIT N&#xff0c; NOWAIT 关键字。但使用时还存在一些限制。例如&#xff1a;LOCK TABLE只能针对单表进行锁定&#xff0c;并不…

URL GET +号后台接收成空格

问题&#xff1a;参数spdmwhbs001 其中包含URL特殊符号 如果用GET请求方式不做任何不处理那么浏览器自动将转为%20 请求链接为 details?spdmwhbs%20001&limitKcysType1 后台接收到的参数为 whbs 001 &#xff0c;自动将号转成空格了。 尝试解决&#xff08;失败&#…

Redis中的事务(二)

事务 事务的实现 执行事务 当一个处于事务状态的客户端向服务器发送EXEC命令时&#xff0c;这个EXEC命令将立即被服务器执行&#xff0c;服务器会遍历这个客户端的事务队列&#xff0c;执行队列中保存的所有命令&#xff0c;最后将执行命令所得的结果全部返回给客户端。 例…

STM32学习和实践笔记(17):STM32外部中断(EXTI)的整体介绍

1.外部中断介绍 1.1 EXTI简介 STM32F10x外部中断/事件控制器&#xff08;EXTI&#xff09;包含多达 20 个用于产生事件/中断请求的边沿检测器。&#xff08;事件与中断的区别&#xff0c;可参看STM32---中断与事件的区别_中断和事件的区别-CSDN博客&#xff09; 具体有哪些&a…

C语言结课实战项目_贪吃蛇小游戏

目录 最终实现效果&#xff1a; 实现基本的功能&#xff1a; 根据游戏进程解释代码&#xff1a; 游戏初始化&#xff1a; 首先进入游戏&#xff0c;我们应该将窗口名称改为 “贪吃蛇” 并将光标隐藏掉。再在中间打印游戏信息。 之后我们要把地图打印出来&#xff1a; 然后…