0
点赞
收藏
分享

微信扫一扫

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

乌龙茶3297 03-08 13:00 阅读 2

文章目录

每日一言

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


跟着课程敲了一下午,终于写好了~
用到了一些没用过的函数,总结介绍一下

⭐首先是设置光标位置,它可以让我们指哪打哪,非常好用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 (会用一点点就行)

代码:

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();

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);
	}
}

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

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

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

结语

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


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

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

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


举报

相关推荐

C语言--贪吃蛇

C语言——贪吃蛇

【c语言】贪吃蛇

贪吃蛇(C语言)

【贪吃蛇:C语言实现】

0 条评论