当前位置:   article > 正文

c语言实现贪吃蛇小游戏————附全代码!!!_c游戏代码大全

c游戏代码大全

目录

1.Win32 API

1.1控制台应用程序

1.2控制台的名称,控制台窗口大小

 1.3设置控制台光标位置

COORD - 光标坐标

GetStdHandle - 获取句柄

SetConsoleCursorPosition - 设置光标位置

封装一个设置光标的函数

1.4设置控制台光标的属性

CONSOLE_CURSOR_INFO - 光标属性

SetConsoleCursorInfo - 设置光标属性

1.5 GetAsyncKeyState - 获取按键状态

2.贪吃蛇游戏注意事项讲解

 2.1地图的设计

2.1.1setlocale

2.1.2宽字符的打印

 2.1.3地图的坐标

2.2 贪吃蛇的设计

2.3贪吃蛇 与 食物 在地图上的打印

3.贪吃蛇游戏流程设计

4.贪吃蛇全代码:

Snake.h

Snake.c

Snaketest.c


注意事项:

  • 本项目使用的环境是vs2022
  • 该项目下的控制台程序需要改成 :Windows 控制台主机 

1.Win32 API

在此项目中我们需要用到 Win32 API 中的一些函数完成对控制台应用程序的操作,接下来我们将挨个介绍

Win32 API(Windows 32-bit Application Programming Interface)是微软为Windows操作系统提供的一套应用程序编程接口(API)。它允许开发者使用C或C++等编程语言来编写Windows桌面应用程序。Win32 API涵盖了从基本的窗口管理、图形绘制到更高级的网络编程、文件I/O和线程同步等各种功能。

1.1控制台应用程序

控制台应用程序的一些属性:

1.控制台名称,控制的窗口大小

2.控制台光标位置

3.控制台光标的大小属性

除了控制台名称以外的属性,接下来我们都要使用Win32 API 中的函数 来对它们进行修改

1.2控制台的名称,控制台窗口大小

设置控制台窗口的⼤⼩,30行,100列 :
mode con cols= 100 lines= 30

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

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. }

 1.3设置控制台光标位置

想要精确的定位控制台光标的位置,我们首先要知道控制台是有坐标轴的。

有了坐标轴的概念,我们才能更好的去设置控制

COORD - 光标坐标

COORD是 Windows API 中定义的⼀个结构体,表示⼀个字符在控制台屏幕幕缓冲区上的坐标

COORD类型的声明:

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

创建一个COORD类型的结构体类型变量,并赋值:

COORD pos = { 10, 15 };

GetStdHandle - 获取句柄

包含 <windows.h> 头文件中 

 函数原型:HANDLE GetStdHandle(DWORD nStdHandle);

GetStdHandle函数为Windows API 中的一个函数,它用于获取特定设备上(标准输入,标准输出,标准错误)中获取一个句柄(用来获取设备不同的数值),使用这个句柄可以操作设备

参数:

 DWORD nStdHandle:标准设备,此参数的取值可为下列值之一

含义
STD_INPUT_HANDLE标准输入设备,通常为键盘
STD_OUTPUT_HANDLE标准输出设备,通常为屏幕
STD_ERROR_HANDLE标准错误设备,通常为屏幕

返回值: 

HANDLE : 该返回值其实是一个指向HANDLE结构体的指针

经过重命名 --- typedef void *HANDLE 为 HANDLE

也可以把它称为一个句柄,通过句柄我们可以修改控制台的属性

使用: 

  1. int main() {
  2. //创建一个HANDLE指针变量
  3. HANDLE hOutput = NULL;
  4. //获取标准输出的句柄(⽤来标识不同设备的数值)
  5. hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  6. return 0;
  7. }

SetConsoleCursorPosition - 设置光标位置

包含 <windows.h> 头文件中

函数原型:

BOOL WINAPI SetConsoleCursorPosition (
    HANDLE hConsoleOutput,
    COORD pos
);

设置屏幕缓冲区新的光标位置 

参数:

HANDLE hConsoleOutput : HANDLE指针变量(控制台屏幕的句柄)

COORD pos : 指定新光标位置(以字符为单位)的 COORD 结构。

返回值:

如果该函数成功,则返回值为非零值。

如果函数失败,则返回值为零。 

使用:

将光标的位置定位到 x = 10 y = 5 的位置

  1. int main(){
  2. //创建一个HANDLE指针变量
  3. HANDLE hOutput = NULL;
  4. //获取标准输出的句柄(⽤来标识不同设备的数值)
  5. hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  6. //设置COORD结构体变量的 x y 值
  7. COORD pos = { 10 , 5 };
  8. //设置新的光标位置
  9. SetConsoleCursorPosition(hOutput, pos);
  10. getchar();
  11. }

封装一个设置光标的函数

  1. //设置光标的坐标
  2. void SetPos(short x, short y)
  3. {
  4. //获取标准输出的句柄
  5. HANDLE hOutput = NULL;
  6. hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  7. //设置标准输出上光标的位置为pos
  8. COORD pos = { x, y };
  9. SetConsoleCursorPosition(hOutput, pos);
  10. }

1.4设置控制台光标的属性

CONSOLE_CURSOR_INFO - 光标属性

这个结构体包含控制台光标的属性
typedef struct _ CONSOLE_CURSOR_INFO {
    DWORD dwSize;
    BOOL bVisible;
} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;

成员 DWORD dwSize --- 由光标填充的字符单元格的百分⽐。 此值介于1到100之间。 光标外观会变化,范围从完全填充单元格到单元底部的⽔平线条。

成员 BOOL bVisible --- 游标的可⻅性。 如果光标可⻅,则此成员为 true,如果光标不可见,则此成员为 false

创建一个COORD类型的结构体类型变量,并赋值:

  1. int main(){
  2. //创建一个CONSOLE_CURSOR_INFO结构体变量,并且赋值
  3. CONSOLE_CURSOR_INFO CursorInfo = {50 , false };
  4. return 0
  5. }

SetConsoleCursorInfo - 设置光标属性

包含 <windows.h> 头文件中

函数原型:

BOOL WINAPI SetConsoleCursorInfo (
    HANDLE hConsoleOutput,
    const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo
);

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

参数: 

HANDLE hConsoleOutput :HANDLE指针变量(控制台屏幕的句柄)

const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo :指向 CONSOLE_CURSOR_INFO 结构的指针,该结构为控制台屏幕缓冲区的光标提供新的规范。

返回值 :

如果该函数成功,则返回值为非零值。

如果函数失败,则返回值为零。

使用 :

隐藏控制台光标操作

  1. int main(){
  2. //影藏光标操作
  3. HANDLE hOutput = NULL;
  4. //获取标准输出的句柄(⽤来标识不同设备的数值)
  5. hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  6. CONSOLE_CURSOR_INFO CursorInfo = { 1,false };//创建结构体,并赋值以你想要设置的控制台光标属性
  7. SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标新的属性
  8. return 0;
  9. }

1.5 GetAsyncKeyState - 获取按键状态

 包含 <windows.h> 头文件中

函数原型:

SHORT GetAsyncKeyState (
    int vKey
);

确定调用函数时键是向上还是向下,以及上次调用 GetAsyncKeyState 后是否按下了该键。

 参数:

int vKey --- 键盘上各个按键的虚拟键码   具体虚拟键码点击此处查看

返回值: 

函数的返回值是一个SHORT类型,表示指定键的状态。如果指定的键被按下,则返回值的最高位(位15)为1,同时如果自上次调用GetAsyncKeyState以来键已被按过,则最低位(位0)也为1。如果键未被按下,则返回值为0。因此,如果返回值是负数,表示该键此前被按下并一直保持按下状态。

使用:

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

写一个宏,用来判断一个键是否被按过
  1. //原理 : 判断返回值的最低为是否为1,是的话返回 1,不是的话返回 0
  2. #define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )

2.贪吃蛇游戏注意事项讲解

我们最终实现的游戏大概长这个样子

 

 2.1地图的设计

在游戏地图上,我们打印墙体使用的是宽字符:□ ,打印蛇使用宽字符 :● ,打印食物使用宽字符:☆
普通的字符是占⼀个字节的,这类宽字符是占用2个字节。( wchar_t --- 宽字符类型
那么我们要怎么打印出宽字符呢?这时就需要用到 setlocale 函数,对编译器进行本地化了

2.1.1setlocale

包含 <locale.h> 头文件中

函数原型:char* setlocale (int category, const char* locale);

对编译器进行本地化设置,编译器才能支持我们宽字符(汉字)的输出

参数 :

int category :

受影响的类项,主要有下面这几个参数

参数受影响的类项
LC_COLLATE
影响字符串⽐较函数 strcoll() strxfrm()
LC_CTYPE
影响字符处理函数的⾏为
LC_MONETARY
影响货币格式
LC_NUMERIC
影响 printf() 的数字格式
LC_TIME
影响时间格式 strftime() wcsftime()
LC_ALL
针对所有类项修改,将以上所有类别设置为给定的语⾔环境

const char* locale : 

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

在任意程序开始执行的时候都会隐藏执行调用:

setlocale(LC_ALL, "C");

返回值 :

成功后,指向 C 字符串的指针

如果函数无法设置新的区域设置,则不会修改此设置,并返回空指针

使用:

设置本地化模式,后就⽀持宽字符(汉字)的输出等。

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

2.1.2宽字符的打印

宽字符的定义:

宽字符数据类型为 wchar_t 当我们定义一个宽字符常量的时候,我们要在应号之前加上 :L

例如:

  1. #include <stdio.h>
  2. #include<locale.h>
  3. int main() {
  4. setlocale(LC_ALL, "");
  5. wchar_t ch1 = L'●';
  6. wchar_t ch2 = L'玖';
  7. wchar_t ch3 = L'伍';
  8. wchar_t ch4 = L'★';
  9. return 0
  10. }

宽字符的打印:

宽字符的打印需要用到函数 wprintf () ,用法与printf ()类似

宽字符的占位符为:%lc

例外在函数wprintf()格式的前面也要加上 :L  ,才能正确的打印宽字符

例如:

  1. #include <stdio.h>
  2. #include<locale.h>
  3. int main() {
  4. setlocale(LC_ALL, "");
  5. wchar_t ch1 = L'●';
  6. wchar_t ch2 = L'玖';
  7. wchar_t ch3 = L'伍';
  8. wchar_t ch4 = L'★';
  9. wprintf(L"%lc\n", ch1);
  10. wprintf(L"%lc\n", ch2);
  11. wprintf(L"%lc\n", ch3);
  12. wprintf(L"%lc\n", ch4);
  13. return 0;
  14. }

 2.1.3地图的坐标

例如我们想实现27行 ,58 列的一个贪吃蛇地图,在围绕着地图填充墙体

注意:

一个窄字符在屏幕缓冲区上是一个长方形

一个宽字符在屏幕缓冲区上占两个窄字符

所以当我们围绕贪吃蛇地图填充墙的时候,会变成图上这样

2.2 贪吃蛇的设计

设计一个结构体来管理我们的贪吃蛇

  1. //蛇的身体
  2. typedef struct SnakeNode {
  3. int x;
  4. int y;
  5. struct SnakeNode* next;
  6. }SnakeNode,* pSnakeNode;
  7. //蛇头的方向
  8. enum DIRECTION {
  9. UP = 1,
  10. DOWN,
  11. LEFT,
  12. RIGHT
  13. };
  14. //贪吃蛇的状态
  15. enum GAME_STATUSL {
  16. OK,//正常状态
  17. KILL_BY_WALL,//撞到墙死了
  18. KILL_BY_SELF,//撞到自己死了
  19. END_NORMAL//游戏正常退出
  20. };
  21. //贪吃蛇对象
  22. typedef struct Snake {
  23. pSnakeNode _pSnake; //指向蛇头的指针
  24. pSnakeNode _pFood; //指向食物节点的指针
  25. enum DIRECTION _dir; //蛇头的方向
  26. enum GAME_STATUSL _status; //游戏此时的状态
  27. int _food_weight; //一个食物的分数
  28. int _score; //游戏总分
  29. int _sleep_time; //游戏休眠的时间,休眠时间越短,速度越快,休眠时间越长,速度越慢
  30. }Snake,* pSnake;

贪吃蛇的属性我们选着利用结构体来维护它

2.3贪吃蛇 与 食物 在地图上的打印

初始化状态,假设蛇的⻓度是5,蛇⾝的每个节点是●,在固定的⼀个坐标处,⽐如(24, 5)处开始出现蛇,连续5个节点。
关于⻝物,就是在墙体内随机⽣成⼀个坐标(x坐标必须是2的倍数),坐标不能和蛇的⾝体重合,然后打印★。
注意:蛇的每个节点的x坐标必须是2个倍数,否则可能会出现蛇的⼀个节点有⼀半⼉出现在墙体中, 另外⼀般在墙外的现象,坐标不好对齐。食物也是如此

3.贪吃蛇游戏流程设计

4.贪吃蛇全代码:

Snake.h

  1. #pragma once
  2. #include<stdio.h>
  3. #include<windows.h>
  4. #include<stdlib.h>
  5. #include<stdbool.h>
  6. #include<locale.h>
  7. #include<time.h>
  8. #define WALL L'□'
  9. #define pos_x 24
  10. #define pos_y 5
  11. #define BODY L'●'
  12. #define FOOD L'☆'
  13. #define KEY_PRESS(VK) ((GetAsyncKeyState(VK) & 1) ? 1 : 0)
  14. //蛇的身体
  15. typedef struct SnakeNode {
  16. int x;
  17. int y;
  18. struct SnakeNode* next;
  19. }SnakeNode, * pSnakeNode;
  20. //蛇头的方向
  21. enum DIRECTION {
  22. UP = 1,
  23. DOWN,
  24. LEFT,
  25. RIGHT
  26. };
  27. //贪吃蛇的状态
  28. enum GAME_STATUSL {
  29. OK,//正常状态
  30. KILL_BY_WALL,//撞到墙死了
  31. KILL_BY_SELF,//撞到自己死了
  32. END_NORMAL//游戏正常退出
  33. };
  34. //贪吃蛇对象
  35. typedef struct Snake {
  36. pSnakeNode _pSnakeHead; //指向蛇头的指针
  37. pSnakeNode _pFood; //指向食物节点的指针
  38. enum DIRECTION _dir; //蛇头的方向
  39. enum GAME_STATUSL _status; //游戏此时的状态
  40. int _food_weight; //一个食物的分数
  41. int _score; //游戏总分
  42. int _sleep_time; //游戏休眠的时间,休眠时间越短,速度越快,休眠时间越长,速度越慢
  43. }Snake, * pSnake;
  44. //1.游戏开始
  45. void GameStart(pSnake ps);
  46. //定位光标函数
  47. void SetPos(short x,short y);
  48. //打印欢迎界面
  49. WelcomeToGame();
  50. //地图绘制
  51. CreateMap();
  52. //创建一条贪吃蛇,并且部分初始化
  53. void InitSnake(pSnake ps);
  54. //创建食物
  55. void CreatFood(pSnake ps);
  56. //2.游戏运行
  57. void GameRun(pSnake ps);
  58. //暂停
  59. void Pause();
  60. //蛇走一步的过程
  61. void SnakeMove(pSnake ps);
  62. //判断下一个位置是不是食物
  63. int NextIsFood(pSnakeNode pn, pSnake ps);
  64. //吃掉食物
  65. void EatFood(pSnakeNode pn, pSnake ps);
  66. //不是食物走一步
  67. void NoFood(pSnakeNode pn, pSnake ps);
  68. //检测蛇是否撞到墙
  69. void KillByWall(pSnake ps);
  70. //检测蛇是否撞到自己
  71. void KillBySelf(pSnake ps);
  72. //3.游戏结束
  73. void GameOvre(pSnake ps);

Snake.c

  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include"Snake.h"
  3. //定位光标函数
  4. void SetPos(short x,short y) {
  5. //获取句柄
  6. HANDLE hOutput = NULL;
  7. hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  8. //设置标准输出上光标的位置为pos
  9. COORD pos = { x, y };
  10. SetConsoleCursorPosition(hOutput, pos);
  11. }
  12. //打印欢迎界面
  13. WelcomeToGame() {
  14. SetPos(35,13);
  15. wprintf(L"欢迎来到贪吃蛇小游戏");
  16. SetPos(36, 17);
  17. system("pause");
  18. system("cls");
  19. SetPos(24, 13);
  20. wprintf(L"按↑ . ↓ . ← . → 来控制蛇的移动,F3加速 ,F4减速");
  21. SetPos(35, 14);
  22. wprintf(L"加速能够得到更高的分数");
  23. SetPos(36, 17);
  24. system("pause");
  25. system("cls");
  26. }
  27. //绘制地图
  28. CreateMap() {
  29. //上墙
  30. for (int i = 0; i < 29; i++)
  31. {
  32. wprintf(L"%lc", WALL);
  33. }
  34. //下墙
  35. SetPos(0,26);
  36. for (int i = 0; i < 29; i++)
  37. {
  38. wprintf(L"%lc", WALL);
  39. }
  40. //左墙
  41. for (int i = 1; i <= 25; i++)
  42. {
  43. SetPos(0,i);
  44. wprintf(L"%lc", WALL);
  45. }
  46. //右墙
  47. for (int i = 1; i <= 25; i++)
  48. {
  49. SetPos(56, i);
  50. wprintf(L"%lc", WALL);
  51. }
  52. }
  53. //创建一条贪吃蛇,并且部分初始化
  54. void InitSnake(pSnake ps) {
  55. //初始化蛇头方向,一个食物分数,总分数,屏幕休眠时间,蛇状态
  56. ps->_dir = RIGHT;
  57. ps->_food_weight = 10;
  58. ps->_score = 0;
  59. ps->_sleep_time = 200;
  60. ps->_status = OK;
  61. //初始化蛇身
  62. pSnakeNode cur = NULL;
  63. for (int i = 0; i < 5; i++)
  64. {
  65. cur = (pSnakeNode)malloc(sizeof(SnakeNode));//创建新的蛇身节点
  66. if (cur == NULL)
  67. {
  68. perror("InitSnak()::malloc()");
  69. return;
  70. }
  71. //设置蛇身节点坐标,即让每个节点成为新的蛇头
  72. cur->x = pos_x + i * 2; //每次新的节点x轴坐标都比前一个蛇身节点多2
  73. cur->y = pos_y;
  74. cur->next = NULL;
  75. //使用头插法把蛇身节点都连接起来
  76. if (ps->_pSnakeHead == NULL)
  77. {
  78. ps->_pSnakeHead = cur; //蛇身一个节点都没有的情况
  79. }
  80. else
  81. {
  82. cur->next = ps->_pSnakeHead;
  83. ps->_pSnakeHead = cur;
  84. }
  85. }
  86. //将蛇打印在地图上
  87. cur = ps->_pSnakeHead;
  88. while (cur)
  89. {
  90. SetPos(cur->x,cur->y);
  91. wprintf(L"%lc",BODY);
  92. cur = cur->next;
  93. }
  94. }
  95. //创建食物
  96. void CreatFood(pSnake ps) {
  97. int x;
  98. int y;
  99. //为食物创建随机的坐标
  100. again:
  101. do
  102. {
  103. //食物的坐标要在墙体内
  104. x = rand() % 53 + 2; //x轴的范围是 2 ~ 54
  105. y = rand() % 25 + 1; //y轴的范围是 1 ~ 25
  106. } while (x % 2 != 0);
  107. //检查食物坐标是否与蛇身重叠
  108. pSnakeNode cur = ps->_pSnakeHead;
  109. while (cur)
  110. {
  111. if ((cur->x == x) && (cur->y == y))
  112. {
  113. goto again;//若重叠,则重新为食物创建坐标
  114. }
  115. cur = cur->next;
  116. }
  117. //创建食物节点
  118. pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));
  119. if (pFood == NULL)
  120. {
  121. perror("CreatFood()::malloc()");
  122. }
  123. pFood->x = x;
  124. pFood->y = y;
  125. pFood->next = NULL;
  126. //记录食物节点到贪吃蛇中
  127. ps->_pFood = pFood;
  128. //打印食物
  129. SetPos(x,y);
  130. wprintf(L"%lc", FOOD);
  131. }
  132. //一.游戏开始
  133. void GameStart(pSnake ps) {
  134. //1.设置窗口大小,名称
  135. system("mode con cols=100 lines=30");
  136. system("title 贪吃蛇");
  137. //2.隐藏光标
  138. HANDLE hOutput = NULL;
  139. //获取标准输出的句柄(⽤来标识不同设备的数值)
  140. hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  141. CONSOLE_CURSOR_INFO CursorInfo = { 1,false }; //创建结构体,并赋值以你想要设置的控制台光标属性
  142. SetConsoleCursorInfo(hOutput, &CursorInfo);
  143. //3.打印欢迎界面
  144. WelcomeToGame();
  145. //4.地图绘制
  146. CreateMap();
  147. //5.将贪吃蛇属性部分初始化
  148. InitSnake(ps);
  149. //6.创建食物
  150. CreatFood(ps);
  151. /*getchar();*/
  152. }
  153. PrintfHelpInfo() {
  154. SetPos(62, 10);
  155. wprintf(L"%ls", L"不能穿墙,不能撞到自己");
  156. SetPos(62, 11);
  157. wprintf(L"%ls", L"按↑ . ↓ . ← . → 来控制蛇的移动");
  158. SetPos(62, 12);
  159. wprintf(L"%ls", L"F3加速 ,F4减速");
  160. SetPos(62, 13);
  161. wprintf(L"%ls", L"按Esc退出游戏,按空格暂停游戏");
  162. SetPos(62, 16);
  163. wprintf(L"%ls", L"———玖伍出品———");
  164. }
  165. //暂停
  166. void Pause() {
  167. while (1) {
  168. Sleep(200);//为了省性能加上屏幕睡眠时间,否者一直死循环浪费新能
  169. if (KEY_PRESS(VK_SPACE))
  170. {
  171. break;
  172. }
  173. }
  174. }
  175. //判断下一个位置是不是食物
  176. int NextIsFood(pSnakeNode pn, pSnake ps) {
  177. return ((pn->x == ps->_pFood->x) && (pn->y == ps->_pFood->y));
  178. }
  179. //吃掉食物
  180. //pSnakeNode psn 是计算的下⼀个节点的地址
  181. //pSnake ps 维护蛇的指针
  182. void EatFood(pSnakeNode pn, pSnake ps) {
  183. //头插法,将即将要吃掉的食物节点成为新的头结点
  184. ps->_pFood->next = ps->_pSnakeHead;//食物节点成为新的头节点
  185. ps->_pSnakeHead = ps->_pFood; //改变贪吃蛇的蛇头属性
  186. //打印蛇
  187. pSnakeNode cur = ps->_pSnakeHead;
  188. while (cur)
  189. {
  190. SetPos(cur->x, cur->y);
  191. wprintf(L"%c", BODY); //第一次循环就将原来的食物节点覆盖了,不用做过多的处理
  192. cur = cur->next;
  193. }
  194. //吃到食物加总分
  195. ps->_score += ps->_food_weight;
  196. //释放我们计算的下一个节点
  197. free(pn);
  198. pn = NULL;
  199. //创建新的食物
  200. CreatFood(ps);
  201. }
  202. //不是食物走一步
  203. void NoFood(pSnakeNode pn, pSnake ps) {
  204. //走一步的过程:将计算的下一个节点头插,打印新的蛇(除了尾节点),最后在释放尾节点
  205. //将计算的下一个节点成为新的蛇头
  206. pn->next = ps->_pSnakeHead;
  207. ps->_pSnakeHead = pn;
  208. //打印的蛇头,覆盖食物的图案
  209. pSnakeNode cur = ps->_pSnakeHead;
  210. SetPos(cur->x, cur->y);
  211. wprintf(L"%lc", BODY);
  212. //找到新蛇倒数第二个节点,我们后面要让他成为新的尾节点,和释放旧的尾节点
  213. while (cur->next->next != NULL)
  214. {
  215. cur = cur->next;
  216. }
  217. //清除屏幕上残留的旧尾节点图案
  218. SetPos(cur->next->x, cur->next->y);
  219. printf(" ");
  220. //释放尾节点
  221. free(cur->next);
  222. cur->next = NULL;
  223. //将新为节点的next指向NULL
  224. cur->next = NULL;
  225. }
  226. //检测蛇是否撞到墙
  227. void KillByWall(pSnake ps) {
  228. if (ps->_pSnakeHead->x == 0 || ps->_pSnakeHead->x == 56 ||
  229. ps->_pSnakeHead->y == 0 || ps->_pSnakeHead->y == 26)
  230. {
  231. ps->_status = KILL_BY_WALL;
  232. }
  233. }
  234. //检测蛇是否撞到自己
  235. void KillBySelf(pSnake ps) {
  236. pSnakeNode cur = ps->_pSnakeHead->next;
  237. while (cur)
  238. {
  239. //若蛇头与某个蛇身体节点重叠就撞到自己了
  240. if (cur->x == ps->_pSnakeHead->x && cur->y == ps->_pSnakeHead->y)
  241. {
  242. ps->_status = KILL_BY_SELF;
  243. break;
  244. }
  245. cur = cur->next;
  246. }
  247. }
  248. //蛇走一步的过程
  249. void SnakeMove(pSnake ps) {
  250. //计算蛇头的下一刻的位置
  251. pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));
  252. if (pNextNode == NULL)
  253. {
  254. perror("SnakeMove()::malloc");
  255. }
  256. switch (ps->_dir)
  257. {
  258. case UP:
  259. pNextNode->x = ps->_pSnakeHead->x;
  260. pNextNode->y = ps->_pSnakeHead->y - 1;
  261. break;
  262. case DOWN:
  263. pNextNode->x = ps->_pSnakeHead->x;
  264. pNextNode->y = ps->_pSnakeHead->y + 1;
  265. break;
  266. case RIGHT:
  267. pNextNode->x = ps->_pSnakeHead->x + 2;
  268. pNextNode->y = ps->_pSnakeHead->y;
  269. break;
  270. case LEFT:
  271. pNextNode->x = ps->_pSnakeHead->x - 2;
  272. pNextNode->y = ps->_pSnakeHead->y;
  273. break;
  274. }
  275. //判断下一个位置是不是食物
  276. if (NextIsFood(pNextNode,ps))//是食物此函数返回1,否者返回0
  277. {
  278. //下一个位置是食物,就吃掉
  279. EatFood(pNextNode, ps);
  280. }
  281. else
  282. {
  283. //下一个位置不是食物,蛇整体走一步
  284. NoFood(pNextNode, ps);
  285. }
  286. //检测蛇是否撞到墙
  287. KillByWall(ps);
  288. //检测蛇是否撞到自己
  289. KillBySelf(ps);
  290. }
  291. //2.游戏运行
  292. void GameRun(pSnake ps) {
  293. //1.右侧打印帮助信息
  294. PrintfHelpInfo();
  295. //2.贪吃蛇在地图里行走,当蛇的状态!= OK时,结束行走
  296. do {
  297. //食物分数与总分数随着游戏的进行会被改变,所以要一直打印,直至游戏结束
  298. SetPos(64, 7);
  299. printf("总得分:%d ", ps->_score);
  300. SetPos(64, 8);
  301. printf("每个食物得分:%2d分", ps->_food_weight);
  302. if (KEY_PRESS(VK_UP) && ps->_dir != DOWN)
  303. {
  304. //向上走的蛇方向不能是往下
  305. ps->_dir = UP;
  306. }
  307. else if (KEY_PRESS(VK_DOWN) && ps->_dir != UP)
  308. {
  309. //向下走的蛇方向不能是往上
  310. ps->_dir = DOWN;
  311. }
  312. else if (KEY_PRESS(VK_LEFT) && ps->_dir != RIGHT)
  313. {
  314. //向左走的蛇方向不能是往右
  315. ps->_dir = LEFT;
  316. }
  317. else if (KEY_PRESS(VK_RIGHT) && ps->_dir != LEFT)
  318. {
  319. //向右走的蛇方向不能是往左
  320. ps->_dir = RIGHT;
  321. }
  322. else if (KEY_PRESS(VK_SPACE))
  323. {
  324. //暂停
  325. Pause();
  326. }
  327. else if (KEY_PRESS(VK_ESCAPE))
  328. {
  329. //正常退出游戏
  330. ps->_status = END_NORMAL;
  331. }
  332. else if (KEY_PRESS(VK_F3))
  333. {
  334. //加速
  335. if (ps->_sleep_time > 80)
  336. {
  337. ps->_sleep_time -= 30;
  338. ps->_food_weight += 2;
  339. }
  340. }
  341. else if (KEY_PRESS(VK_F4))
  342. {
  343. //减速
  344. if (ps->_food_weight > 2)
  345. {
  346. ps->_sleep_time += 30;
  347. ps->_food_weight -= 2;
  348. }
  349. }
  350. //蛇走一步的过程
  351. SnakeMove(ps);
  352. Sleep(ps->_sleep_time);
  353. }while(ps->_status == OK);//当状态不是OK时结束行走
  354. }
  355. //3.游戏结束
  356. void GameOvre(pSnake ps) {
  357. SetPos(24, 12);
  358. switch (ps->_status)
  359. {
  360. case END_NORMAL:
  361. printf("您主动结束了游戏");
  362. break;
  363. case KILL_BY_WALL:
  364. printf("您撞上了墙,结束了游戏");
  365. break;
  366. case KILL_BY_SELF:
  367. printf("您撞上了自己,结束了游戏");
  368. break;
  369. }
  370. //释放链表
  371. pSnakeNode cur = ps->_pSnakeHead;
  372. while (cur)
  373. {
  374. pSnakeNode del = cur;
  375. cur = cur->next;
  376. free(del);
  377. }
  378. }

Snaketest.c

  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include"Snake.h"
  3. int main() {
  4. setlocale(LC_ALL, "");
  5. srand((unsigned int)time(NULL));
  6. int ch = 0;
  7. do
  8. {
  9. system("cls");
  10. //游戏开始
  11. Snake snake = { 0 };
  12. GameStart(&snake);
  13. //游戏中
  14. GameRun(&snake);
  15. //游戏结束
  16. GameOvre(&snake);
  17. SetPos(20, 15);
  18. printf("在来一局吗?(Y/N)");
  19. ch = getchar();
  20. //清理回车
  21. while (getchar() != '\n');
  22. } while (ch == 'y' || ch == 'Y');
  23. SetPos(0, 27);
  24. return 0;
  25. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/741730
推荐阅读
相关标签
  

闽ICP备14008679号