当前位置:   article > 正文

C语言实现贪吃蛇游戏

C语言实现贪吃蛇游戏


前言

C语言实现贪吃蛇游戏


一、蛇的结构的创建

  • 先创建一个蛇身的节点
  • 在创建蛇的结构(包括:指向蛇头的指针,指向食物的指针,游戏的状态,蛇的速度,蛇的方向,食物的分数,总得分)

蛇的方向和游戏的状态采用枚举列举。

// 蛇的方向
enum DIRECTION
{
	UP = 1,
	DOWN,
	LEFT,
	RIGHT
};

// 游戏的状态(正常、撞墙、撞到自己、正常退出)
enum GAME_STATUS
{
	ok,//正常状态
	KILL_BY_WALL,// 撞墙死
	KILL_BY_SELF,// 撞到自己死
	END_NORMAL // esc正常退出
};



// 创建蛇身的节点
typedef struct SnakeNode 
{
	// 每个节点坐标
	int x;
	int y;
	// 写一个蛇身节点的地址
	struct SnakeNode* next;
}SnakeNode, *pSnakeNode;


// 真正的贪吃蛇
typedef struct Snake
{
	pSnakeNode _pSnake;// 指向蛇头的指针
	pSnakeNode _pFood; // 指向食物节点的指针
	enum DIRECTION _dir; // 蛇的方向
	enum GAME_STATUS _status;// 当前游戏的状态
	int _food_weight; // 一个食物的分数
	int _score; // 总分数
	int _sleep_time; // 休眠时间,时间越长,速度越慢,时间越短,速度越快
}Snake, *pSnake;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

二、游戏测试的逻辑

  • 每次游戏开始都先清空屏幕
  • 初始化游戏
  • 游戏运行
  • 游戏结束
void test()
{
	int ch = 0;
	do
	{
		system("cls");
		// 创建贪吃蛇
		Snake snake = { 0 };

		// 初始化游戏
		// 1. 打印欢迎界面
		// 2. 打印功能介绍
		// 3. 绘制地图
		// 4. 创建蛇
		// 5. 创建食物
		// 6. 设置游戏的相关信息
		GameStart(&snake);
		// 游戏运行
		GameRun(&snake);
		// 游戏结束--- 游戏的善后工作
		GameEnd(&snake);
		SetPos(25, 18);
		printf("要再来一局吗?(Y/N)");
		ch = getchar();
		while (getchar() != '\n');
	} while (ch == 'Y' || ch == 'y');
	

	SetPos(0, 27);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

三、初始化游戏

  • 打印欢迎界面
  • 打印功能介绍
  • 绘制地图
  • 创建蛇
  • 创建食物
  • 在创建蛇和创建食物同时初始化蛇的基本信息
void SetPos(short x, short y)
{
	// 获得标准输出设备的句柄
	HANDLE houtput = NULL;
	houtput = GetStdHandle(STD_OUTPUT_HANDLE);

	//定位光标的位置
	COORD pos = { x, y };
	SetConsoleCursorPosition(houtput, pos);
}

// 1-2.欢迎界面的打印及功能介绍函数
void WelcomeToGame()
{
	SetPos(40, 14);
	wprintf(L"欢迎来到贪吃蛇小游戏!!\n");
	SetPos(42, 25);
	system("pause");
	system("cls");
	SetPos(30, 14);
	wprintf(L"按↑. ↓. ←. →来控制蛇的方向,按F3加速,F4减速");
	SetPos(40, 16);
	wprintf(L"加速可以得到跟高的分数");
	SetPos(42, 25);
	system("pause");
	system("cls");
	
}


// 3. 打印地图
void CreateMap()
{
	// 上
	int i = 0;
	for (i = 0; i < 29; i++)
	{
		wprintf(L"%lc", WALL);
	}
	// 下
	SetPos(0, 26);
	for (i = 0; i < 29; i++)
	{
		wprintf(L"%lc", WALL);
	}

	// 左
	for (i = 1; i <= 25; i++)
	{
		SetPos(0, i);
		wprintf(L"%lc", WALL);
	}

	// 右
	for (i = 1; i <= 25; i++)
	{
		SetPos(56, i);
		wprintf(L"%lc", WALL);
	}
}

// 4. 创建蛇
void InitSnake(pSnake ps)
{
	pSnakeNode cur = NULL;
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		// 申请一个蛇身节点
		cur = (pSnakeNode)malloc(sizeof(SnakeNode));
		if (NULL == cur)
		{
			perror("InitSnake()::malloc()");
			return;
		}

		cur->next = NULL;
		cur->x = POS_X + 2 * i;
		cur->y = POS_Y;

		// 头插法插入链表
		if (ps->_pSnake == NULL) // 空链表
		{
			ps->_pSnake = cur;
		}
		else // 非空链表
		{
			cur->next = ps->_pSnake;
			ps->_pSnake = cur;
		}
	}
	// 打印蛇身
	cur = ps->_pSnake;

	SetPos(cur->x, cur->y);
	wprintf(L"%lc", HEAD);
	cur = cur->next;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	
	// 设置贪吃蛇属性
	ps->_dir = RIGHT; // 初始方向设置为向右
	ps->_score = 0; // 总成绩初始为0
	ps->_food_weight = 10;// 一个食物10分
	ps->_sleep_time = 200; // 休眠时间,单位为毫秒
	ps->_status = ok; // 默认游戏状态ok

}


// 5. 创建食物函数
void CreateFood(pSnake ps)
{
	int x = 0;
	int y = 0;
again:
	// 随机生成食物位置,不能在墙体上
	do
	{
		x = rand() % 53 + 2;
		y = rand() % 25 + 1;
	} while (x % 2 != 0);

	// 随机生成的食物也不能在蛇身上
	pSnakeNode cur = ps->_pSnake;
	while (cur)
	{
		if (x == cur->x && y == cur->y)
		{
			goto again;
		}
		cur = cur->next;
	}

	// 申请一个食物节点
	pSnakeNode pFood = malloc(sizeof(SnakeNode));
	if (pFood == NULL)
	{
		perror("CreateFood()::malloc()");
		return;
	}

	pFood->next = NULL;
	pFood->x = x;
	pFood->y = y;

	// 定位位置打印食物
	SetPos(pFood->x, pFood->y);
	wprintf(L"%lc", FOOD);

	ps->_pFood = pFood;

}

// 初始化游戏
void GameStart(pSnake ps)
{
	// 0. 先设置窗口大小再光标隐藏
	
	//设置窗口大小
	system("mode con cols=100 lines=30");
	system("title 贪吃蛇");
	
	// 获得标准输出设备的句柄
	HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	// 隐藏光标操作
	CONSOLE_CURSOR_INFO CursorInfo;
	GetConsoleCursorInfo(houtput, &CursorInfo);// 获取控制台光标信息
	CursorInfo.bVisible = false; // 隐藏控制台光标
	SetConsoleCursorInfo(houtput, &CursorInfo);// 设置控制台光标信息


	// 1. 打印欢迎界面
	// 2. 打印功能介绍
	WelcomeToGame();

	// 3. 绘制地图
	CreateMap();
	// 4. 创建蛇
	InitSnake(ps);
	// 5. 创建食物
	CreateFood(ps);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187

四、游戏运行

  • 游戏运行主要包括
  • 打印帮助信息
  • 按键的检测(改变方向)
  • 暂停游戏功能
  • 判断下一个节点是否为食物(吃掉食物、没有食物)
  • 检测是否撞墙
  • 检测是否撞到自己
// 打印帮助信息函数
void PrintHelpInfo()
{
	SetPos(64, 17);
	wprintf(L"不能穿墙,不能咬到自己");
	SetPos(64, 18);
	wprintf(L"按↑. ↓. ←. →来控制蛇的方向");
	SetPos(64, 19);
	wprintf(L"+ 为加速,- 为减速");
	SetPos(64, 20);
	wprintf(L"ESC: 退出游戏!SPACE: 暂停游戏");

	SetPos(64, 22);
	wprintf(L"hhb@版权");
}

// 检测按键的宏
#define KEY_PRESS(vk) ((GetAsyncKeyState(vk) & 1)? 1 : 0)


// 暂停函数
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);
}

// 吃掉食物函数
void EatFood(pSnakeNode pn, pSnake ps)
{
	// 将食物节点头插到蛇头之前
	ps->_pFood->next = ps->_pSnake;
	ps->_pSnake = ps->_pFood;

	// 释放next节点
	free(pn);
	pn = NULL;

	// 打印蛇身
	pSnakeNode cur = ps->_pSnake;
	SetPos(cur->x, cur->y);
	wprintf(L"%lc", HEAD);

	cur = cur->next;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}

	ps->_score += ps->_food_weight;

	// 吃掉食物,再创建一个新的事物
	CreateFood(ps);

}

// 没有食物
void NoFood(pSnakeNode pn, pSnake ps)
{
	// 新的节点头插到链表中
	pn->next = ps->_pSnake;
	ps->_pSnake = pn;


	// 打印蛇
	pSnakeNode cur = ps->_pSnake;

	SetPos(cur->x, cur->y);
	wprintf(L"%lc", HEAD);

	cur = cur->next;
	while (cur->next->next != NULL)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}

	// 将最后一个节点打印成空白
	SetPos(cur->next->x, cur->next->y);
	printf("  ");

	// 释放最后一个节点
	free(cur->next);
	cur->next = NULL;
}


// 检测蛇是否撞墙
void KillByWall(pSnake ps)
{
	if (ps->_pSnake->x == 0 || ps->_pSnake->x == 56 ||
		ps->_pSnake->y == 0 || ps->_pSnake->y == 26)
	{
		ps->_status = KILL_BY_WALL;
	}
}

// 检测蛇是否撞到自己
void KillBySelf(pSnake ps)
{
	pSnakeNode cur = ps->_pSnake->next;

	while (cur)
	{
		if (cur->x == ps->_pSnake->x && cur->y == ps->_pSnake->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()");
		return;
	}

	// 确定下一节点的位置
	switch (ps->_dir)
	{
		case UP:
			pNextNode->x = ps->_pSnake->x;
			pNextNode->y = ps->_pSnake->y - 1;
			break;
		case DOWN:
			pNextNode->x = ps->_pSnake->x;
			pNextNode->y = ps->_pSnake->y + 1;
			break;
		case LEFT:
			pNextNode->x = ps->_pSnake->x - 2;
			pNextNode->y = ps->_pSnake->y;
			break;
		case  RIGHT:
			pNextNode->x = ps->_pSnake->x + 2;
			pNextNode->y = ps->_pSnake->y;
			break;
	}

	// 需要判断下一点是否是食物
	if (NextIsFood(pNextNode, ps))
	{
		EatFood(pNextNode, ps);
	}
	else
	{
		NoFood(pNextNode, ps);
	}


	// 检测蛇是否撞到墙
	KillByWall(ps);
	// 检测蛇是否撞到自己
	KillBySelf(ps);
}



// 游戏运行
void GameRun(pSnake ps)
{
	// 打印帮助信息
	PrintHelpInfo();

	do
	{
		// 打印总得分和食物权重
		SetPos(64, 10);
		wprintf(L"总得分为: %d", ps->_score);
		SetPos(64, 12);
		wprintf(L"当前食物的分数为: %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_ADD))
		{
			// 加速
			if (ps->_sleep_time > 80)
			{
				ps->_sleep_time -= 30;
				ps->_food_weight += 2;
			}

		}
		else if (KEY_PRESS(VK_SUBTRACT))
		{
			// 减速
			if (ps->_food_weight >2 )
			{
				ps->_sleep_time += 30;
				ps->_food_weight -= 2;
			}
		}

		// 蛇向前走走一步
		SnakeMove(ps);
		// 蛇每走一步sleep一下
		Sleep(ps->_sleep_time);


	} while (ps->_status == ok);

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252

五、游戏结束

  • 根据蛇结构中游戏的状态打印对应的信息,并退出
  • 销毁链表
  • 在测试函数中循环执行,并增加提示信息,是否再来一把。
// 游戏结束--游戏善后工作
void GameEnd(pSnake ps)
{
	SetPos(24, 12);
	switch (ps->_status)
	{
	case END_NORMAL:
		wprintf(L"您正常退出了!!\n");
		break;
	case KILL_BY_WALL:
		wprintf(L"您撞到墙上了,游戏结束!!\n");
		break;
	case KILL_BY_SELF:
		wprintf(L"您撞到自己了,游戏结束!!\n");
		break;
	}


	// 销毁链表
	pSnakeNode cur = ps->_pSnake;

	while (cur)
	{
		pSnakeNode del = cur;
		cur = cur->next;
		free(del);
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

《点击查看源码》


总结

C语言实现贪吃蛇游戏

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

闽ICP备14008679号