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来执⾏。例如:

#include <stdio.h>
int main()
{//设置控制台窗⼝的⻓宽:设置控制台窗⼝的⼤⼩,30⾏,100列system("mode con cols=100 lines=30");//设置cmd窗⼝名称system("title 贪吃蛇"); return 0;
}

 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

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

使用: 

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

SetConsoleCursorPosition - 设置光标位置

包含 <windows.h> 头文件中

函数原型:

BOOL WINAPI SetConsoleCursorPosition (
    HANDLE hConsoleOutput,
    COORD pos
);

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

参数:

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

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

返回值:

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

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

使用:

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

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

封装一个设置光标的函数

//设置光标的坐标
void SetPos(short x, short y)
{//获取标准输出的句柄HANDLE hOutput = NULL;hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//设置标准输出上光标的位置为posCOORD pos = { x, y };SetConsoleCursorPosition(hOutput, pos);}

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类型的结构体类型变量,并赋值:

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

SetConsoleCursorInfo - 设置光标属性

包含 <windows.h> 头文件中

函数原型:

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

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

参数: 

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

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

返回值 :

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

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

使用 :

隐藏控制台光标操作

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

1.5 GetAsyncKeyState - 获取按键状态

 包含 <windows.h> 头文件中

函数原型:

SHORT GetAsyncKeyState (
    int vKey
);

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

 参数:

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

返回值: 

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

使用:

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

写一个宏,用来判断一个键是否被按过
//原理 : 判断返回值的最低为是否为1,是的话返回 1,不是的话返回 0
#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

例如:

#include <stdio.h>
#include<locale.h>
int main() {setlocale(LC_ALL, "");wchar_t ch1 = L'●';wchar_t ch2 = L'玖';wchar_t ch3 = L'伍';wchar_t ch4 = L'★';return 0;
}

宽字符的打印:

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

宽字符的占位符为:%lc

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

例如:

#include <stdio.h>
#include<locale.h>int main() {setlocale(LC_ALL, "");wchar_t ch1 = L'●';wchar_t ch2 = L'玖';wchar_t ch3 = L'伍';wchar_t ch4 = L'★';wprintf(L"%lc\n", ch1);wprintf(L"%lc\n", ch2);wprintf(L"%lc\n", ch3);wprintf(L"%lc\n", ch4);return 0;}

 2.1.3地图的坐标

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

注意:

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

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

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

2.2 贪吃蛇的设计

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

//蛇的身体
typedef struct SnakeNode {int x;int y;struct SnakeNode* next;
}SnakeNode,* pSnakeNode;//蛇头的方向
enum DIRECTION {UP = 1,DOWN,LEFT,RIGHT
};//贪吃蛇的状态
enum GAME_STATUSL {OK,//正常状态KILL_BY_WALL,//撞到墙死了KILL_BY_SELF,//撞到自己死了END_NORMAL//游戏正常退出
};//贪吃蛇对象
typedef struct Snake {pSnakeNode _pSnake;             //指向蛇头的指针pSnakeNode _pFood;             //指向食物节点的指针enum DIRECTION _dir;          //蛇头的方向enum GAME_STATUSL _status;    //游戏此时的状态int _food_weight;           //一个食物的分数int _score;                //游戏总分int _sleep_time;          //游戏休眠的时间,休眠时间越短,速度越快,休眠时间越长,速度越慢
}Snake,* pSnake;

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

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

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

3.贪吃蛇游戏流程设计

4.贪吃蛇全代码:

Snake.h

#pragma once
#include<stdio.h>
#include<windows.h>
#include<stdlib.h>
#include<stdbool.h>
#include<locale.h>
#include<time.h>#define WALL L'□'
#define pos_x 24
#define pos_y 5
#define BODY L'●'
#define FOOD L'☆'#define KEY_PRESS(VK) ((GetAsyncKeyState(VK) & 1) ? 1 : 0)//蛇的身体
typedef struct SnakeNode {int x;int y;struct SnakeNode* next;
}SnakeNode, * pSnakeNode;//蛇头的方向
enum DIRECTION {UP = 1,DOWN,LEFT,RIGHT
};//贪吃蛇的状态
enum GAME_STATUSL {OK,//正常状态KILL_BY_WALL,//撞到墙死了KILL_BY_SELF,//撞到自己死了END_NORMAL//游戏正常退出
};//贪吃蛇对象
typedef struct Snake {pSnakeNode _pSnakeHead;             //指向蛇头的指针pSnakeNode _pFood;             //指向食物节点的指针enum DIRECTION _dir;          //蛇头的方向enum GAME_STATUSL _status;    //游戏此时的状态int _food_weight;           //一个食物的分数int _score;                //游戏总分int _sleep_time;          //游戏休眠的时间,休眠时间越短,速度越快,休眠时间越长,速度越慢
}Snake, * pSnake;//1.游戏开始
void GameStart(pSnake ps);//定位光标函数
void SetPos(short x,short y);//打印欢迎界面
WelcomeToGame();//地图绘制
CreateMap();//创建一条贪吃蛇,并且部分初始化
void InitSnake(pSnake ps);//创建食物
void CreatFood(pSnake ps);//2.游戏运行
void GameRun(pSnake ps);//暂停
void Pause();//蛇走一步的过程
void SnakeMove(pSnake ps);//判断下一个位置是不是食物
int NextIsFood(pSnakeNode pn, pSnake ps);//吃掉食物
void EatFood(pSnakeNode pn, pSnake ps);//不是食物走一步
void NoFood(pSnakeNode pn, pSnake ps);//检测蛇是否撞到墙
void KillByWall(pSnake ps);//检测蛇是否撞到自己
void KillBySelf(pSnake ps);//3.游戏结束
void GameOvre(pSnake ps);

Snake.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Snake.h"
//定位光标函数
void SetPos(short x,short y) {//获取句柄HANDLE hOutput = NULL;hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//设置标准输出上光标的位置为posCOORD pos = { x, y };SetConsoleCursorPosition(hOutput, pos);}//打印欢迎界面
WelcomeToGame() {SetPos(35,13);wprintf(L"欢迎来到贪吃蛇小游戏");SetPos(36, 17);system("pause");system("cls");SetPos(24, 13);wprintf(L"按↑ . ↓ . ← . → 来控制蛇的移动,F3加速 ,F4减速");SetPos(35, 14);wprintf(L"加速能够得到更高的分数");SetPos(36, 17);system("pause");system("cls");}//绘制地图
CreateMap() {//上墙for (int i = 0; i < 29; i++){wprintf(L"%lc", WALL);}//下墙SetPos(0,26);for (int i = 0; i < 29; i++){wprintf(L"%lc", WALL);}//左墙for (int i = 1; i <= 25; i++){SetPos(0,i);wprintf(L"%lc", WALL);}//右墙for (int i = 1; i <= 25; i++){SetPos(56, i);wprintf(L"%lc", WALL);}
}//创建一条贪吃蛇,并且部分初始化
void InitSnake(pSnake ps) {//初始化蛇头方向,一个食物分数,总分数,屏幕休眠时间,蛇状态ps->_dir = RIGHT;ps->_food_weight = 10;ps->_score = 0;ps->_sleep_time = 200;ps->_status = OK;//初始化蛇身pSnakeNode cur = NULL;for (int i = 0; i < 5; i++){cur = (pSnakeNode)malloc(sizeof(SnakeNode));//创建新的蛇身节点if (cur == NULL){perror("InitSnak()::malloc()");return;}//设置蛇身节点坐标,即让每个节点成为新的蛇头cur->x = pos_x + i * 2;  //每次新的节点x轴坐标都比前一个蛇身节点多2格cur->y = pos_y;cur->next = NULL;//使用头插法把蛇身节点都连接起来if (ps->_pSnakeHead == NULL){ps->_pSnakeHead = cur;   //蛇身一个节点都没有的情况}else{cur->next = ps->_pSnakeHead;ps->_pSnakeHead = cur;}}//将蛇打印在地图上cur = ps->_pSnakeHead;while (cur){SetPos(cur->x,cur->y);wprintf(L"%lc",BODY);cur = cur->next;}}//创建食物
void CreatFood(pSnake ps) {int x;int y;//为食物创建随机的坐标
again:do {//食物的坐标要在墙体内x = rand() % 53 + 2;  //x轴的范围是 2 ~ 54y = rand() % 25 + 1;  //y轴的范围是 1 ~ 25} while (x % 2 != 0);//检查食物坐标是否与蛇身重叠pSnakeNode cur = ps->_pSnakeHead;while (cur) {if ((cur->x == x) && (cur->y == y)){goto again;//若重叠,则重新为食物创建坐标}cur = cur->next;}//创建食物节点pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));if (pFood == NULL){perror("CreatFood()::malloc()");}pFood->x = x;pFood->y = y;pFood->next = NULL;//记录食物节点到贪吃蛇中ps->_pFood = pFood;//打印食物SetPos(x,y);wprintf(L"%lc", FOOD);}//一.游戏开始
void GameStart(pSnake ps) {//1.设置窗口大小,名称system("mode con cols=100 lines=30");system("title 贪吃蛇");//2.隐藏光标HANDLE hOutput = NULL;//获取标准输出的句柄(⽤来标识不同设备的数值)hOutput = GetStdHandle(STD_OUTPUT_HANDLE);CONSOLE_CURSOR_INFO CursorInfo = { 1,false }; //创建结构体,并赋值以你想要设置的控制台光标属性SetConsoleCursorInfo(hOutput, &CursorInfo);//3.打印欢迎界面WelcomeToGame();//4.地图绘制CreateMap();//5.将贪吃蛇属性部分初始化InitSnake(ps);//6.创建食物CreatFood(ps);/*getchar();*/}
PrintfHelpInfo() {SetPos(62, 10);wprintf(L"%ls", L"不能穿墙,不能撞到自己");SetPos(62, 11);wprintf(L"%ls", L"按↑ . ↓ . ← . → 来控制蛇的移动");SetPos(62, 12);wprintf(L"%ls", L"F3加速 ,F4减速");SetPos(62, 13);wprintf(L"%ls", L"按Esc退出游戏,按空格暂停游戏");SetPos(62, 16);wprintf(L"%ls", L"———玖伍出品———");
}//暂停
void Pause() {while (1) {Sleep(200);//为了省性能加上屏幕睡眠时间,否者一直死循环浪费新能if (KEY_PRESS(VK_SPACE)){break;}}
}//判断下一个位置是不是食物
int NextIsFood(pSnakeNode pn, pSnake ps) {return ((pn->x == ps->_pFood->x) && (pn->y == ps->_pFood->y));
}//吃掉食物
//pSnakeNode psn 是计算的下⼀个节点的地址
//pSnake ps 维护蛇的指针
void EatFood(pSnakeNode pn, pSnake ps) {//头插法,将即将要吃掉的食物节点成为新的头结点ps->_pFood->next = ps->_pSnakeHead;//食物节点成为新的头节点ps->_pSnakeHead = ps->_pFood;      //改变贪吃蛇的蛇头属性//打印蛇pSnakeNode cur = ps->_pSnakeHead;while (cur){SetPos(cur->x, cur->y);wprintf(L"%c", BODY);    //第一次循环就将原来的食物节点覆盖了,不用做过多的处理cur = cur->next;}//吃到食物加总分ps->_score += ps->_food_weight;//释放我们计算的下一个节点free(pn);pn = NULL;//创建新的食物CreatFood(ps);
}//不是食物走一步
void NoFood(pSnakeNode pn, pSnake ps) {//走一步的过程:将计算的下一个节点头插,打印新的蛇(除了尾节点),最后在释放尾节点//将计算的下一个节点成为新的蛇头pn->next = ps->_pSnakeHead;ps->_pSnakeHead = pn;//打印的蛇头,覆盖食物的图案pSnakeNode cur = ps->_pSnakeHead;SetPos(cur->x, cur->y);wprintf(L"%lc", BODY);//找到新蛇倒数第二个节点,我们后面要让他成为新的尾节点,和释放旧的尾节点while (cur->next->next != NULL){cur = cur->next;}//清除屏幕上残留的旧尾节点图案SetPos(cur->next->x, cur->next->y);printf("  ");//释放尾节点free(cur->next);cur->next = NULL;//将新为节点的next指向NULLcur->next = NULL;
}//检测蛇是否撞到墙
void KillByWall(pSnake ps) {if (ps->_pSnakeHead->x == 0 || ps->_pSnakeHead->x == 56 ||ps->_pSnakeHead->y == 0 || ps->_pSnakeHead->y == 26){ps->_status = KILL_BY_WALL;}
}//检测蛇是否撞到自己
void KillBySelf(pSnake ps) {pSnakeNode cur = ps->_pSnakeHead->next;while (cur){//若蛇头与某个蛇身体节点重叠就撞到自己了if (cur->x == ps->_pSnakeHead->x && cur->y == ps->_pSnakeHead->y){ps->_status = KILL_BY_SELF;break;}cur = cur->next;}
}//蛇走一步的过程
void SnakeMove(pSnake ps) {//计算蛇头的下一刻的位置pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));if (pNextNode == NULL){perror("SnakeMove()::malloc");}switch (ps->_dir){case UP:pNextNode->x = ps->_pSnakeHead->x;pNextNode->y = ps->_pSnakeHead->y - 1;break;case DOWN:pNextNode->x = ps->_pSnakeHead->x;pNextNode->y = ps->_pSnakeHead->y + 1;break;case RIGHT:pNextNode->x = ps->_pSnakeHead->x + 2;pNextNode->y = ps->_pSnakeHead->y;break;case LEFT:pNextNode->x = ps->_pSnakeHead->x - 2;pNextNode->y = ps->_pSnakeHead->y;break;}//判断下一个位置是不是食物if (NextIsFood(pNextNode,ps))//是食物此函数返回1,否者返回0{//下一个位置是食物,就吃掉EatFood(pNextNode, ps);}else{//下一个位置不是食物,蛇整体走一步NoFood(pNextNode, ps);}//检测蛇是否撞到墙KillByWall(ps);//检测蛇是否撞到自己KillBySelf(ps);
}
//2.游戏运行
void GameRun(pSnake ps) {//1.右侧打印帮助信息PrintfHelpInfo();//2.贪吃蛇在地图里行走,当蛇的状态!= OK时,结束行走do {//食物分数与总分数随着游戏的进行会被改变,所以要一直打印,直至游戏结束SetPos(64, 7);printf("总得分:%d ", ps->_score);SetPos(64, 8);printf("每个食物得分:%2d分", ps->_food_weight);if (KEY_PRESS(VK_UP) && ps->_dir != DOWN){//向上走的蛇方向不能是往下ps->_dir = UP;}else if (KEY_PRESS(VK_DOWN) && ps->_dir != UP){//向下走的蛇方向不能是往上ps->_dir = DOWN;}else if (KEY_PRESS(VK_LEFT) && ps->_dir != RIGHT){//向左走的蛇方向不能是往右ps->_dir = LEFT;}else if (KEY_PRESS(VK_RIGHT) && ps->_dir != LEFT){//向右走的蛇方向不能是往左ps->_dir = RIGHT;}else if (KEY_PRESS(VK_SPACE)){//暂停Pause();}else if (KEY_PRESS(VK_ESCAPE)){//正常退出游戏ps->_status = END_NORMAL;}else if (KEY_PRESS(VK_F3)){//加速if (ps->_sleep_time > 80){ps->_sleep_time -= 30;ps->_food_weight += 2;}}else if (KEY_PRESS(VK_F4)){//减速if (ps->_food_weight > 2){ps->_sleep_time += 30;ps->_food_weight -= 2;}}//蛇走一步的过程SnakeMove(ps);Sleep(ps->_sleep_time);}while(ps->_status == OK);//当状态不是OK时结束行走}//3.游戏结束
void GameOvre(pSnake ps) {SetPos(24, 12);switch (ps->_status){case END_NORMAL:printf("您主动结束了游戏");break;case KILL_BY_WALL:printf("您撞上了墙,结束了游戏");break;case KILL_BY_SELF:printf("您撞上了自己,结束了游戏");break;}//释放链表pSnakeNode cur = ps->_pSnakeHead;while (cur){pSnakeNode del = cur;cur = cur->next;free(del);}
}

Snaketest.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Snake.h"int main() {setlocale(LC_ALL, "");srand((unsigned int)time(NULL));int ch = 0;do{system("cls");//游戏开始Snake snake = { 0 };GameStart(&snake);//游戏中GameRun(&snake);//游戏结束GameOvre(&snake);SetPos(20, 15);printf("在来一局吗?(Y/N)");ch = getchar();//清理回车while (getchar() != '\n');} while (ch == 'y' || ch == 'Y');SetPos(0, 27);return 0;
}

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

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

相关文章

栈PART 1

目录 1. 栈 1.1 栈的概念和结构 1.2 栈的实现 1.2.1 栈的顺序储存结构 1.2.2 栈的基本操作 1.3 有效的括号 1. 栈 1.1 栈的概念和结构 堆栈又名栈&#xff08;stack&#xff09;&#xff0c;它是一种运算受限的线性表。 限定仅在表尾进行插入和删除操作的线性表。这一端…

C++进阶之路:深入理解编程范式,从面向过程到面向对象(类与对象_上篇)

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

java-函数式编程-语法

目录 1、函数表现形式 分类 lambda表达式 参数类型可以全写&#xff0c;也可以全不写&#xff0c;但不能一部分写&#xff0c;一部分不写lambda 的省略策略&#xff1a;凡是可推导&#xff0c;都可以省略 方法引用 练习-判断语法正确性 练习-写出与方法引用等价的lambda表达式…

TCP三次握手四次挥手 UDP

TCP是面向链接的协议&#xff0c;而UDP是无连接的协议 TCP的三次握手 三次传输过程是纯粹的不涉及数据&#xff0c;三次握手的几个数据包中不包含数据内容。它的应用层&#xff0c;数据部分是空的&#xff0c;只是TCP实现会话建立&#xff0c;点到点的连接 TCP的四次挥手 第四…

VMware虚拟机提示内存不足

VMware虚拟机&#xff0c;k8s集群搭建内存不足的问题 疑问&#xff1a;我的电脑是8G8G双通道的内存&#xff0c;当我在搭建k8s集群时给master-2G内存&#xff0c;node1-3G内存&#xff0c;node2-3G内存&#xff1b; 当依次打开虚拟机到node2时VM提示“物理内存不足&#xff0c;…

【大学物理】双语合集听课笔记

7.5 angular momentu(角动量)_哔哩哔哩_bilibili 6.4Energy in Rotation Motion 有质量有速度的物体有动能&#xff0c;是不是很有道理 international system&#xff08;from French systeme international&#xff0c;acronym&#xff0c;SI&#xff09;of ineria kg*m^2 转…

使用 Cython 加密 Python 代码防止反编译

文章目录 前言使用 Cython 加密 Python 代码环境Python 源代码编写 Cython 编译配置文件 编译查看输出文件使用 问题error: Microsoft Visual C 14.0 or greater is requiredpyconfig.h(59): fatal error C1083: 无法打开包括文件: “io.h”: No such file or directorydynamic…

鸿蒙开发全攻略:华为应用系统如何携手嵌入式技术开启新篇章~

鸿蒙操作系统是华为自主创新的成果&#xff0c;打破了传统操作系统的局限。通过结合嵌入式技术&#xff0c;鸿蒙实现了跨平台、跨设备的高度融合&#xff0c;提供了流畅、智能的体验。华为应用系统与嵌入式技术的结合&#xff0c;提升了性能&#xff0c;丰富了用户体验。鸿蒙与…

嵌入式Linux的QT项目CMake工程模板分享及使用指南

在嵌入式linux开发板上跑QT应用&#xff0c;不同于PC上的开发过程。最大的区别就是需要交叉编译&#xff0c;才能在板子上运行。 这里总结下嵌入式linux环境下使用CMake&#xff0c;嵌入式QT的CMake工程模板配置及如何使用&#xff0c;分享给有需要的小伙伴&#xff0c;有用到的…

【C++】---继承

【C】---继承 一、继承的概念及定义1、继承的概念2、定义语法格式3、继承基类成员访问方式的变化 二、基类 和 派生类 的对象之间的赋值转换1、赋值规则2、切片&#xff08;1&#xff09;子类对象 赋值 给 父类对象&#xff08;2&#xff09;子类对象 赋值 给 父类指针&#xf…

数据结构-线性表-链表-2.3-1

设计一个递归算法&#xff0c;删除不带头结点的单链表L中所有值为x的结点。 void del(Linkllist &L&#xff0c;int x){LNode *p;if(LNULL){return;}if(L->datax){pL;LL->next;;free(p);del(L,x);}else{del(L->next,x);} } 时间复杂度为O(n)

【项目学习01_2024.05.08_Day06】

学习笔记 5 新增课程5.1 需求分析5.1.1 业务流程5.1.2 数据模型 5.2 接口定义5.3 接口开发5.3.1 保存课程基本信息5.3.2 保存营销信息 5.4 接口测试 5 新增课程 5.1 需求分析 5.1.1 业务流程 5.1.2 数据模型 5.2 接口定义 5.3 接口开发 根据需求分析&#xff0c;新增课程表…

模型智能体开发之metagpt-多智能体实践

参考&#xff1a; metagpt环境配置参考模型智能体开发之metagpt-单智能体实践 需求分析 之前有过单智能体的测试case&#xff0c;但是现实生活场景是很复杂的&#xff0c;所以单智能体远远不能满足我们的诉求&#xff0c;所以仍然还需要了解多智能体的实现。通过多个role对动…

NSSCTF中的web

目录 [第五空间 2021]WebFTP [LitCTF 2023]PHP是世界上最好的语言&#xff01;&#xff01; [SWPUCTF 2021 新生赛]PseudoProtocols [LitCTF 2023]导弹迷踪 [NISACTF 2022]easyssrf [第五空间 2021]WebFTP 1.进入页面&#xff0c;发现是登录页面&#xff0c;想到 弱口令&…

《MySQL45讲》读书笔记

重建表 alter table t engine InnoDB&#xff08;也就是recreate&#xff09;&#xff0c;而optimize table t 等于recreateanalyze&#xff0c;让表大小变小 重建表的执行流程 建立一个临时文件&#xff0c;扫描表 t 主键的所有数据页&#xff1b;用数据页中表 t 的记录生…

六步轻松打造创新文化和领导力的技术团队

引言 在当今快速变化的商业环境中&#xff0c;构建一个支持创新的文化和领导力至关重要。通过实施创新激励机制、建立失败的容忍度、领导层的示范作用、开放和透明的沟通、促进多样性和包容性以及持续学习和适应&#xff0c;公司可以培养出一支能够不断创新的员工队伍&#xf…

6层板学习笔记2

说明:笔记基于6层全志H3消费电子0.65MM间距BGA 67、多层板的电源建议直接大面积铺铜,不建议走线,铺铜充分满足其载流能力 68、凡亿推荐表层1OZ的铜厚线宽20MIL能承载1A的电流,内层0.5OZ的铜厚线宽为40MIL能承载1A的电流,过孔直径20MIL(0.5MM)能承载1A左右的电流,实际设…

QT实战百度语音识别

前言 随着学习的深入&#xff0c;感觉愈发缺乏满足感。刚好看到微信语音转文字的功能&#xff0c;经网上查询&#xff0c;发现可以使用 QT 百度语音识别技术 实现这一功能。当然&#xff0c;由于使用的 QT 和 百度语音识别&#xff0c;那么看不到一些具体的底层实现&#xff…

C++初识多态(1)

1.多态要解决的问题&#xff08;引入&#xff09; 任何一种机制的存在&#xff0c;必然是有其存在的意义的&#xff0c;例如我们前面学过的函数重载&#xff0c;运算符重载&#xff0c;以及引用等等&#xff0c;都是解决一些特殊问题的&#xff1b; 下面通过一些具体的例子&a…

Echarts散点图的29个配置项,散点图可以随心所欲啦。

1-9 当使用 ECharts 绘制散点图时&#xff0c;可以配置以下一些常用的选项&#xff1a; 1. tooltip&#xff1a;配置提示框组件&#xff0c;用于显示鼠标悬停在散点上时的提示信息。 2. legend&#xff1a;配置图例组件&#xff0c;用于展示不同散点的标识和名称。 3. xAxis…