C语言贪吃蛇实现,以及一些有趣的想法(可加速减速)

小明 2025-05-04 02:21:07 25

文章目录

  • 每日一言
  • 代码:
    • Snake.h
    • Snake.c
    • test.c
    • 可以增加游戏趣味性的想法
    • ���语

      每日一言

      结局每隔一段时间就会到来,我经历它,就像飞机穿过云层。

      ()

      跟着课程敲了一下午,终于写好了~

      用到了一些没用过的函数,总结介绍一下

      ()

      ⭐首先是设置光标位置,它可以让我们指哪打哪,非常好用awa,以下是一个封装好的函数

      //设置光标位置
      void SetPos(int y, int x) {
      	//获得设备句柄
      	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
      	//根据句柄设置光标的位置
      	COORD pos = { x,y };//先行后列更符合习惯
      	SetConsoleCursorPosition(handle, pos);
      }
      

      ⭐接着是隐藏光标,光标总是一闪一闪的实在是有点烦(bushi

      以下是让光标消失的魔法

      //以下为隐藏光标代码
      //控制台光标信息
      CONSOLE_CURSOR_INFO cursor_info = { 0 };
      //获得设备句柄
      HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
      GetConsoleCursorInfo(handle, &cursor_info);
      //隐藏光标
      cursor_info.bVisible = false;
      //将更改应用到对应的控制台
      SetConsoleCursorInfo(handle, &cursor_info);
      

      ⭐接下来是修改命令框大小与设置命令框标题

      //设置命令框大小
      	system("mode con cols=100 lines=50");
      	
      //设置命令框标题
      	system("title 贪吃蛇");
      

      ⭐然后是设置本地环境,让你可以打印出宽字符,需要包含#include

      //设置本地环境,设置后支持宽字符的使用
      setlocale(LC_ALL, "");
      

      ⭐还有最重要的如何检测一个键是否被按过,以下是一个被定义的宏,通过它来检查按键

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

      VK代表虚拟键值

      以上只是这些函数的简单介绍,如果想详细了解的可以点击🌳虚拟键码

      ⭐接下来是要写完整个贪吃蛇用到的知识

      1. C语言(重要)
      2. 数据结构中的单链表(重要)
      3. WIN32 API (会用一点点就行)

      代码:

      Snake.h

      #pragma once
      #include
      #include
      #include
      #include
      #include
      #include
      #include
      //检测一个键是否被按过
      #define KEY_PRESS(VK) ((GetAsyncKeyState(VK)) & 1 ? 1 : 0)
      //方向
      enum DIRECTION {
      	UP = 1,
      	DOWN,
      	LEFT,
      	RIGHT
      };
      //游戏状态
      enum GAME_STATUS
      {
      	OK = 1,
      	KILL_BY_WALL,
      	KILL_BY_SELF,
      	GAMEOVER
      };
      //蛇身
      typedef struct SnakeNode {
      	int x;
      	int y;
      	struct SnakeNode* next;
      }SnakeNode, * pSnakeNode;
      //整条蛇
      typedef struct Snake{
      	int Score;//分数
      	int FoodWight;//一个食物的分数
      	int SleepTime;//每走一步需要的时间
      	pSnakeNode pSnake;//维护整条蛇的指针
      	pSnakeNode pFood;//维护食物的指针
      	enum DIRECTION Dir;//方向
      	enum GAME_STATUS Status;//游戏状态
      }Snake, * pSnake;
      //设置光标位置
      void SetPos(int x, int y);
      //游戏开始前的准备工作
      void GameStart(pSnake snake);
      //游戏运行时的维护工作
      void GameRun(pSnake snake);
      //游戏结束时的维护工作
      void GameEnd();
      

      Snake.c

      #include "Snake.h"
      //设置光标位置
      void SetPos(int y, int x) {
      	//获得设备句柄
      	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
      	//根据句柄设置光标的位置
      	COORD pos = { x,y };//先行后列更符合习惯
      	SetConsoleCursorPosition(handle, pos);
      }
      //打印地图
      void PrintMap() {
      	int i = 0;
      	//上
      	SetPos(0, 0);
      	for (i = 0; i FoodWight = 10;
      	snake->Dir = RIGHT;
      	snake->Score = 0;
      	snake->SleepTime = 200;
      	snake->Status = OK;
      	//创建蛇身(单链表)
      	pSnakeNode cur = NULL;
      	int i = 0;
      	for (i = 0; i x = 5;
      		cur->y = 24 + i * 2;
      		//头插法
      		cur->next = NULL;
      		if (snake->pSnake == NULL) {
      			snake->pSnake = cur;
      		}
      		else {
      			cur->next = snake->pSnake;
      			snake->pSnake = cur;
      		}
      	}
      	//打印蛇的身体
      	cur = snake->pSnake;
      	while (cur) {
      		SetPos(cur->x, cur->y);
      		wprintf(L"●");
      		cur = cur->next;
      	}
      }
      //创建食物
      void CreateFood(pSnake snake) {
      	int x = 0;
      	int y = 1;
      roll:
      	//生成食物坐标
      	//列应为2的倍数
      	while (y % 2 != 0) {
      		x = rand() % 25 + 1;
      		y = rand() % 53 + 2;
      	}
      	//食物不能生成在蛇身上
      	pSnakeNode cur = snake->pSnake;
      	while (cur) {
      		//如果与蛇身重复,则重新生成食物坐标
      		if (cur->x == x && cur->y == y)
      		{
      			goto roll;
      		}
      		cur = cur->next;
      	}
      	//创建食物
      	pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));
      	if (!pFood) {
      		perror("CreateFood() pFood malloc fail");
      		return;
      	}
      	else {
      		//设置食物坐标
      		pFood->x = x;
      		pFood->y = y;
      		//printf("x = %d y = %d", x, y);
       
      		//打印食物
      		SetPos(pFood->x, pFood->y);
      		wprintf(L"☆");
      		//将食物信息给到游戏
      		snake->pFood = pFood;
      	}
      	//SetPos(27, 0);
      }
      //游戏开始前的准备工作
      void GameStart(pSnake snake) {
      	//以下为隐藏光标代码
      	//控制台光标信息
      	CONSOLE_CURSOR_INFO cursor_info = { 0 };
      	//获得设备句柄
      	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
      	GetConsoleCursorInfo(handle, &cursor_info);
      	//隐藏光标
      	cursor_info.bVisible = false;
      	//将更改应用到对应的控制台
      	SetConsoleCursorInfo(handle, &cursor_info);
      	//设置命令框大小
      	system("mode con cols=100 lines=30");
      	//设置命令框题目
      	system("title 贪吃蛇");
      	//游戏介绍
      	WelcomeToGame();
      	//打印地图
      	PrintMap(snake);
      	//初始化蛇
      	InitSnake(snake);
      	//创建食物
      	CreateFood(snake);
      }
      //打印游戏说明
      void PrintHelpInfo() {
      	SetPos(15, 64);
      	printf("不能碰墙,不能咬到自己");
      	SetPos(16, 64);
      	printf("用↑.↓.←.→ 分别控制蛇的移动");
      	SetPos(17, 64);
      	printf("F3为加速,F4为减速");
      	SetPos(18, 64);
      	printf("ESC :退出游戏.space:暂停游戏.");
      }
      //判断下一个位置是不是食物
      bool IsFood(pSnake snake, pSnakeNode pNextNode) {
      	if (snake->pFood->x == pNextNode->x && snake->pFood->y == pNextNode->y) {
      		return true;
      	}
      	return false;
      }
      //吃食物
      void EatFood(pSnake snake,pSnakeNode pNextNode) {
      	//头插
      	pNextNode->next = snake->pSnake;
      	snake->pSnake = pNextNode;
      	//打印蛇
      	pSnakeNode cur = snake->pSnake;
      	while (cur) {
      		SetPos(cur->x, cur->y);
      		wprintf(L"●");
      		cur = cur->next;
      	}
      	//加分数
      	snake->Score += snake->FoodWight;
      	//释放掉食物节点占用的内存
      	free(snake->pFood);
      	snake->pFood = NULL;
      	//创建一个新的食物
      	CreateFood(snake);
      }
      //下个位置不是食物
      void NotEatFood(pSnake snake, pSnakeNode pNextNode) {
      	//头插
      	pNextNode->next = snake->pSnake;
      	snake->pSnake = pNextNode;
      	//找到尾节点的前一个节点,顺便打印蛇身
      	pSnakeNode cur = snake->pSnake;
      	while (cur->next->next != NULL) {
      		SetPos(cur->x, cur->y);
      		wprintf(L"●");
      		cur = cur->next;
      	}
      	//在蛇尾打印空格
      	SetPos(cur->next->x, cur->next->y);
      	printf("  ");
      	//释放尾节点
      	free(cur->next);
      	cur->next = NULL;
      }
      void SnakeMove(pSnake snake) {
      	//创建下一个节点
      	pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));
      	if (!pNextNode) {
      		perror("SnakeMove() pNextNode malloc fail");
      		return;
      	}
      	//确定下一个节点的位置
      	switch (snake->Dir) {
      		case UP:
      		{
      			pNextNode->x = snake->pSnake->x - 1;
      			pNextNode->y = snake->pSnake->y;
      			break;
      		}
      		case DOWN:
      		{
      			pNextNode->x = snake->pSnake->x + 1;
      			pNextNode->y = snake->pSnake->y;
      			break;
      		}
      		case LEFT:
      		{
      			pNextNode->x = snake->pSnake->x;
      			pNextNode->y = snake->pSnake->y - 2;
      			break;
      		}
      		case RIGHT:
      		{
      			pNextNode->x = snake->pSnake->x;
      			pNextNode->y = snake->pSnake->y + 2;
      			break;
      		}
      	}
      	//判断下一个节点是不是食物
      	if (IsFood(snake,pNextNode))
      	{
      		//吃食物
      		EatFood(snake, pNextNode);
      	}
      	else {
      		//不是食物就正常走
      		NotEatFood(snake, pNextNode);
      	}
      	//判断是否撞墙
      	if (snake->pSnake->x == 26 ||
      		snake->pSnake->x == 0 ||
      		snake->pSnake->y == 0 ||
      		snake->pSnake->y == 56)
      	{
      		snake->Status = KILL_BY_WALL;
      		return;
      	}
      	//判断是否撞到自己
      	pSnakeNode cur = snake->pSnake->next;
      	while (cur) {
      		if (pNextNode->x == cur->x && pNextNode->y == cur->y) {
      			snake->Status = KILL_BY_SELF;
      			return;
      		}
      		cur = cur->next;
      	}
      }
      void pause() {
      	while (1) {
      		Sleep(300);
      		//按空格退出暂停
      		if (KEY_PRESS(VK_SPACE))
      		{
      			break;
      		}
      	}
      }
      void GameRun(pSnake snake) {
      	//打印游戏说明
      	PrintHelpInfo();
      	//蛇的移动
      	do {
      		//打印得分与食物分值
      		SetPos(10, 64);
      		printf("得分: %5d ", snake->Score);
      		printf("每个食物分值:%2d分", snake->FoodWight);
      		//判断玩家是否要改变蛇的方向
      		//按上不能往下
      		if (KEY_PRESS(VK_UP) && snake->Dir != DOWN) {
      			snake->Dir = UP;
      		}
      		else if (KEY_PRESS(VK_DOWN) && snake->Dir != UP) {
      			snake->Dir = DOWN;
      		}
      		else if (KEY_PRESS(VK_LEFT) && snake->Dir != RIGHT) {
      			snake->Dir = LEFT;
      		}
      		else if (KEY_PRESS(VK_RIGHT) && snake->Dir != LEFT) {
      			snake->Dir = RIGHT;
      		}
      		else if (KEY_PRESS(VK_SPACE)) {
      			//暂停
      			pause();
      		}
      		else if (KEY_PRESS(VK_F3)) {
      			//加速
      			if (snake->SleepTime >= 50)
      			{
      				snake->SleepTime -= 30;
      				snake->FoodWight += 2;
      			}
      		}
      		else if (KEY_PRESS(VK_F4)) {
      			//减速
      			if (snake->SleepTime 
      				snake-SleepTime += 30;
      				snake->FoodWight -= 2;
      			}
      		}
      		else if (KEY_PRESS(VK_ESCAPE)) {
      			snake->Status = GAMEOVER;
      			return;
      		}
      		//休眠一下
      		Sleep(snake->SleepTime);
      		//蛇走一步
      		SnakeMove(snake);
      	} while (snake->Status == OK);
      }
      //游戏结束时的维护工作
      void GameEnd(pSnake snake) {
      	//判断玩家是怎么输的
      	if (snake->Status == KILL_BY_SELF) {
      		SetPos(10, 18);
      		printf("很可惜,你咬到自己了");
      	}
      	else if (snake->Status == KILL_BY_WALL) {
      		SetPos(10, 18);
      		printf("很可惜,你被墙撞死了");
      	}
      	else if (snake->Status == GAMEOVER) {
      		SetPos(10, 18);
      		printf("你选择了退出游戏");
      	}
      	//释放蛇身的节点
      	pSnakeNode cur = snake->pSnake;
      	while (cur) {
      		pSnakeNode del = cur;
      		cur = cur->next;
      		free(del);
      	}
      }
      

      test.c

      #include "Snake.h"
      //int main() {
      //	//设置命令框大小
      //	system("mode con cols=100 lines=50");
      //	//设置命令框标题
      //	system("title 贪吃蛇");
      //
      //	//COORD pos = { 40,10 };
      //
      //	//控制台光标信息 结构体
      //	CONSOLE_CURSOR_INFO cursor_info = { 0 };
      //
      //	//获得设备句柄
      //	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
      //
      //	GetConsoleCursorInfo(handle, &cursor_info);
      //
      //	//设置光标大小
      //	//cursor_info.dwSize = 50;
      //
      //	//隐藏光标
      //	cursor_info.bVisible = false;
      //
      //	//设置控制台
      //	SetConsoleCursorInfo(handle, &cursor_info);
      //
      //	//根据句柄设置光标的位置
      //	COORD pos = { 20,5 };
      //	SetConsoleCursorPosition(handle, pos);
      //	
      //
      //	printf("12345");
      //
      //
      //	getchar();
      //	return 0;
      //}
      //int main() {
      //	//设置本地环境,设置后支持宽字符的使用
      //	setlocale(LC_ALL, "");
      //	int i = 0;
      //	for (i = 0; i  
      

      可以增加游戏趣味性的想法

      1. 在场上随机投放道具,吃到了可以获得2次穿墙机会(传送门)
      2. 在场上随机投放道具,吃到了令玩家的按键功能相反(按上蛇往下走)
      3. 可以吃道具,扩大地图,或者随着蛇的增大地图随之变大
      4. 给贪吃蛇染上新颜色
      5. 可以增加贪吃蛇选皮肤界面
      6. 可以吃道具,吃到后蛇分成两半,一边用wasd键操控,一边用上下左右键操控(该道具整局只在一条蛇时概率出现)
      7. 增加音效

      结语

      请给自己些耐心,一口吃不成胖子。

      山外青山楼外楼,莫把百尺当尽头。

      保持空杯心态加油努力吧!


      都看到这里啦!真棒(*^▽^*)

      可以给作者一个免费的赞赞吗,这将会鼓励我继续创作,谢谢大家

      编程小白写作,如有纰漏或错误,欢迎指正


The End
微信