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<locale.h>

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

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

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

VK代表虚拟键值

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

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

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

1. 代码:

1.1 Snake.h

#pragma once

#include<stdio.h>
#include<Windows.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
#include<locale.h>
#include<time.h>

//检测一个键是否被按过
#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();

1.2 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 < 58; i+=2) {
		wprintf(L"%c",L'■');
	}

	//下
	SetPos(26, 0);
	for (i = 0; i < 58; i+=2) {
		wprintf(L"%c", L'■');
	}

	//左
	for (i = 1; i < 26; i++) {
		SetPos(i, 0);
		wprintf(L"%c", L'■');
	}

	//右
	for (i = 1; i < 26; i++) {
		SetPos(i, 56);
		wprintf(L"%c", L'■');
	}
}

//游戏介绍
void WelcomeToGame() {
	SetPos(13, 40);
	printf("欢迎来到贪吃蛇小游戏");
	SetPos(18, 41);
	system("pause");//暂停一下
	system("cls");//清空屏幕
	SetPos(12, 25);
	printf("用↑ . ↓ . ← . → 分别控制蛇的移动, F3为加速,F4为减速");
	SetPos(13, 25);
	printf("加速将能得到更高的分数。");
	SetPos(18, 40);
	system("pause");//暂停一下
	system("cls");//清空屏幕
}

//初始化蛇
void InitSnake(pSnake snake) {
	//初始化游戏数据
	snake->FoodWight = 10;
	snake->Dir = RIGHT;
	snake->Score = 0;
	snake->SleepTime = 200;
	snake->Status = OK;

	//创建蛇身(单链表)
	pSnakeNode cur = NULL;
	int i = 0;
	for (i = 0; i < 2; i++) {
		//创建蛇身节点
		cur = (pSnakeNode)malloc(sizeof(SnakeNode));
		if (!cur) {
			perror("InitSnake() cur malloc fail");
			return;
		}

		//设置蛇的出生地
		cur->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 <= 320)
			{
				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);
	}
}

1.3 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 < 60; i++)
//	{
//		SetPos(i, 0);
//		wprintf(L"%c", L'■');
//	}
//	for (i = 0; i < 30; i++)
//	{
//		SetPos(0 , i);
//		wprintf(L"%c", L'■');
//	}
//
//	return 0;
//}

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

	//设置随机数种子
	srand((unsigned int)time(NULL));

	int op = 0;
	do {
		//设置一条蛇
		Snake snake = { 0 };

		//游戏开始前的准备工作
		GameStart(&snake);

		//游戏运行时的维护工作
		GameRun(&snake);

		//游戏结束时的维护工作
		GameEnd(&snake);
		SetPos(13, 18);
		printf("重开一局?(Y/N):");
		op = getchar();//接收选项
		//拿走缓冲区内的换行
		getchar();
	} while (op == 'Y' || op == 'y');
	SetPos(15, 24);
	printf("退出游戏");

	SetPos(27, 0);

	return 0;
}

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

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

结语

请给自己些耐心,一口吃不成胖子。
山外青山楼外楼,莫把百尺当尽头。
保持空杯心态加油努力吧!


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

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

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


  • 8
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 在C语言编写的贪吃蛇游戏中,当贪吃蛇吃到一定分数的食物时,游戏会出现如下的加速效果。 贪吃蛇是一种经典的游戏,玩家需要控制蛇的移动方向,使其吃到食物并不断变长。一开始,贪吃蛇的移动速度较慢,但随着贪吃蛇吃到越来越多的食物,分数也会逐渐增加。当分数达到一定阈值时,游戏会出现加速效果,使得贪吃蛇的移动速度变快。 实现这个功能,首先需要获得游戏中的贪吃蛇得分。在C语言中,可以使用一个整数变量来代表分数。每当贪吃蛇吃到食物时,分数加一,并将新的分数显示在游戏界面上。 接下来,需要设置一个阈值来判断何时触发加速效果。当分数达到设定的阈值时,加速效果就会被触发。可以使用一个if语句来比较当前分数和阈值的大小,如果大于等于阈值,则加速。 在C语言中,加速效果可以通过两种方式实现。一种是改变贪吃蛇的移动速度,可以通过调整移动的时间间隔来实现加速。另一种是改变贪吃蛇的移动步长,可以通过增加每次移动的格子数来实现加速。 在游戏的循环中,需要不断检测当前分数是否达到了阈值,如果达到了,则将移动速度或步长进行调整,以实现加速效果。可以使用条件语句或循环控制语句来实现这个逻辑。 总之,通过适当设置阈值,并在达到一定分数时实现贪吃蛇加速效果,可以增加游戏的难度和挑战性,让游戏更加有趣。 ### 回答2: 在C语言编写贪吃蛇游戏时,可以通过设置一定的分数目标来触发食物加速的功能。当贪吃蛇吃到的食物数量达到一定的分数时,游戏可以加速。 在游戏开始时,可以设定一个初始的游戏速度。当贪吃蛇吃到食物时,可以通过判断当前得分是否达到设定的分数目标来确定是否进行加速操作。当得分达到目标时,可以通过减小时间延迟来加快游戏的速度。这样,在每次移动时,贪吃蛇的移动就会更加迅速。 在程序编码上,可以使用一个变量来记录当前的得分,并与设定的分数目标进行比较。当得分超过或等于设定的目标分数时,通过相应的代码来修改时间延迟的数值,从而实现加速的效果。 比如,可以使用一个if语句判断当前得分和目标分数的关系,当达到目标分数时,修改一个控制时间延迟的变量的数值,从而实现加速,具体代码如下: ``` int score = 0; // 记录得分 int targetScore = 100; // 分数目标 int delay = 100; // 初始时间延迟 // 游戏循环 while (1) { // 贪吃蛇移动逻辑 // ... // 判断是否达到分数目标 if (score >= targetScore) { // 达到目标分数,加速 delay -= 10; targetScore += 100; // 更新目标分数,为下一次加速做准备 } // 控制移动的速度 sleep(delay); } ``` 在以上代码中,通过不断地比较得分和目标分数的大小关系,可以在达到目标分数时不断地减小时间延迟的值,从而实现游戏的加速效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

月临水

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值