当前位置:   article > 正文

C++入门——实现贪吃蛇游戏_c++贪吃蛇最简单代码

c++贪吃蛇最简单代码

参考

  1. 《C和C++游戏趣味编程》

贪吃蛇游戏

键盘控制小蛇上、下、左、右移动,迟到食物后长度加1;蛇头碰到自身或窗口边缘,游戏失败

程序框架

#include <graphics.h>
#include <conio.h>
#include <stdio.h>

// 全局变量定义

void startup()                       // 初始化函数
{

}

void show()                          // 绘制函数
{
	
}

void updateWithoutInput()            // 与输入无关的更新
{


}

void updateWithInput()               // 和输入有关的更新
{

}

int main()
{
	startup();                       // 初始化函数,仅执行一次
	while (1)
	{
		show();                      // 进行绘制
		updateWithoutInput();        // 和输入无关的更新
		updateWithInput();           // 和输入有关的更新
	}
	return 0;
}
  • 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

绘制游戏地图和蛇

绘制网格状的游戏地图,使用二维数组Blocks存储每个网格的信息。二维数组Blocks中也可以记录蛇的信息。设定元素值为0表示空,画出灰色的方格;元素值为1表示蛇头,蛇头后的蛇身依次为2、3、4、5等正整数,画出彩色的方格

int i, j;
for (i = 0; i < HEIGHT; i++)
{
	for (j = 0; j < WIDTH; j++)
	{
		if (Blocks[i][j] > 0)
		{
			setfillcolor(HSVtoRGB(Blocks[i][j] * 10, 0.9, 1));
		}
		else
		{
			setfillcolor(RGB(150, 150, 150));
		}
		fillrectangle(j * BLOCK_SIZE, i * BLOCK_SIZE, (j + 1) * BLOCK_SIZE, (i + 1) * BLOCK_SIZE);
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在这里插入图片描述

小蛇向右移动

假设小蛇初始元素值为54321,其中1位蛇头,5432位蛇身。首先将二维数组中所有大于0的元素加1,得到65432;然后将最大值6变成0,即去除了原来的蛇尾;最后将2右边的元素由0变成1,即实现了小蛇向右移动

void moveSnake()
{
	int i, j;
	for (i = 0; i < HEIGHT; i++)
	{
		for (j = 0; j < WIDTH; j++)
		{
			if (Blocks[i][j] > 0)
			{
				Blocks[i][j]++;
			}
		}
	}
	int oldTail_i, oldTail_j, oldHead_i, oldHead_j;
	int max = 0;
	for (i = 0; i < HEIGHT; i++)
	{
		for (j = 0; j < WIDTH; j++)
		{
			if (max < Blocks[i][j])
			{
				max = Blocks[i][j];
				oldTail_i = i;
				oldTail_j = j;
			}
			if (Blocks[i][j] == 2)
			{
				oldHead_i = i;
				oldHead_j = j;
			}
		}
	}
	int newHead_i = oldHead_i;
	int newHead_j = oldHead_j;
	newHead_j = oldHead_j + 1;
	Blocks[newHead_i][newHead_j] = 1;
	Blocks[oldTail_i][oldTail_j] = 0;
}
void updateWithoutInput()            // 与输入无关的更新
{
	moveSnake();
	Sleep(100);
}
  • 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

控制小蛇4个方向移动

变量oldHead_i、oldHead_j存储移动前的蛇头位置,newHead_i、newHead_j存储移动后的蛇头位置。小蛇向上移动,只需把新蛇头的坐标设为旧蛇头的上方即可

newHead_i = oldHead_i - 1;
  • 1

让玩家用A、S、D、W键控制游戏角色移动,定义字符变量moveDirection表示小蛇运动方向,在moveSnake函数中对其值进行判断,取A向左运动、D向右运动、W向上运动、S向下运动:

if (moveDirection == 'A')
{
	newHead_j = oldHead_j - 1;
}
else if (moveDirection == 'D')
{
	newHead_j = oldHead_j + 1;
}
else if (moveDirection == 'W')
{
	newHead_i = oldHead_i - 1;
}
else if (moveDirection == 'S')
{
	newHead_i = oldHead_i + 1;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在updateWithInput()函数中获得用户按键输入,如果是A、S、D、W键之一,就更新moveDirection变量,执行moveSnake()函数让小蛇向对应方向移动:

void updateWithInput()               // 和输入有关的更新
{
	if (_kbhit())
	{
		char input = _getch();
		if (input == 'A' || input == 'S' || input == 'D' || input == 'W')
		{
			moveDirection = input;
			moveSnake();
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

时间控制的改进

在Sleep()函数运行时,整个程序都会暂停,包括用户输入模块。用户会感觉到卡顿

利用静态变量,将updateWithoutInput()修改如下:

void updateWithoutInput()            // 与输入无关的更新
{
	static int waitIndex = 1;
	waitIndex++;                     // 每一帧加1
	if (waitIndex == 10)
	{
		moveSnake();
		waitIndex = 1;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

其中,updateWithoutInput()每次运行时,waitIndex加1,每隔10帧,才执行一次移动函数moveSnake()。这样可在不影响用户按键输入的情况下,降低小蛇的移动速度

失败判断与显示

定义全局变量isFailure表示游戏是否失败,初始化为0:

int isFailure = 0;
  • 1

当小蛇碰到画面边界时,则认为游戏失败;当蛇头与蛇身发生碰撞时,游戏也失败。由于每次只有蛇头是新生成的位置,所以在moveSnake()函数中只需判断蛇头是否越过边界和碰撞:

	if (newHead_i >= HEIGHT || newHead_i < 0 || newHead_j >= WIDTH || newHead_j < 0 || Blocks[newHead_i][newHead_j] > 0) 
	{
		isFailure = 1;
		return;
	}show()函数中添加游戏失败后的显示信息:
	if (isFailure)                   // 游戏失败
	{
		setbkmode(TRANSPARENT);      // 文字字体透明
		settextcolor(RGB(255, 0, 0));
		settextstyle(80, 0, _T("宋体"));
		outtextxy(240, 220, _T("游戏失败"));
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

在updateWithoutInput()中添加代码,当isFailure为1时,直接返回:

void updateWithoutInput()            // 与输入无关的更新
{
	if (isFailure)
	{
		return;
	}
	//...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在updateWithInput()中,只有当按下键盘且isFailure为0时,才进行相应的处理:

void updateWithInput()               // 和输入有关的更新
{
	if (_kbhit() && isFailure == 0)
	{
		// ...
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

添加食物

添加全局变量记录食物的位置:

int food_i, food_j;startup()函数中初始化食物的位置:

void startup()                       // 初始化函数
{
	food_i = rand() % (HEIGHT - 5) + 2;
	food_j = rand() % (WIDTH - 5) + 2;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在show()函数中在食物位置处绘制一个绿色小方块:

setfillcolor(RGB(0, 255, 0));
fillrectangle(food_j * BLOCK_SIZE, food_i * BLOCK_SIZE, (food_j + 1) * BLOCK_SIZE, (food_i + 1) * BLOCK_SIZE);
  • 1
  • 2

当新蛇头碰到食物时,只需保留原蛇尾,即可让蛇的长度加1。当吃到食物时,食物位置重新随机出现,蛇长度加1;当没有迟到食物时,旧蛇尾变成空白,蛇长度保持不变:

Blocks[newHead_i][newHead_j] = 1;// 新蛇头位置数值为1
if (newHead_i == food_i && newHead_j == food_j) // 如果新蛇头碰到食物
{
	food_i = rand() % (HEIGHT - 5) + 2; // 食物重新随机位置
	food_j = rand() % (WIDTH - 5) + 2;
}
else
{
	Blocks[oldTail_i][oldTail_j] = 0;   // 旧蛇尾变成空白
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

完整代码

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
#define BLOCK_SIZE 20                // 每个小格子的长宽
#define HEIGHT 30                    // 高度上一共30个小格子
#define WIDTH 40                     // 宽度上一共40个小格子

// 全局变量定义
int Blocks[HEIGHT][WIDTH] = { 0 };
char moveDirection;
int isFailure = 0;
int food_i, food_j;                  // 食物的位置

void startup()                       // 初始化函数
{
	int i;
	Blocks[HEIGHT / 2][WIDTH / 2] = 1;  // 画面中间画蛇头
	for (i = 1; i <= 4; i++)            // 向左依次4个蛇身
	{
		Blocks[HEIGHT / 2][WIDTH / 2 - i] = i + 1;
	}
	moveDirection = 'D';
	food_i = rand() % (HEIGHT - 5) + 2;
	food_j = rand() % (WIDTH - 5) + 2;
	initgraph(WIDTH * BLOCK_SIZE, HEIGHT * BLOCK_SIZE);
	setlinecolor(RGB(200, 200, 200));
	BeginBatchDraw();                   // 开始批量绘制
}

void show()                          // 绘制函数
{
	cleardevice();
	int i, j;
	for (i = 0; i < HEIGHT; i++)
	{
		for (j = 0; j < WIDTH; j++)
		{
			if (Blocks[i][j] > 0)
			{
				setfillcolor(HSVtoRGB(Blocks[i][j] * 10, 0.9, 1));
			}
			else
			{
				setfillcolor(RGB(150, 150, 150));
			}
			fillrectangle(j * BLOCK_SIZE, i * BLOCK_SIZE, (j + 1) * BLOCK_SIZE, (i + 1) * BLOCK_SIZE);
		}
	}
	setfillcolor(RGB(0, 255, 0));    // 食物颜色为绿色
	fillrectangle(food_j * BLOCK_SIZE, food_i * BLOCK_SIZE, (food_j + 1) * BLOCK_SIZE, (food_i + 1) * BLOCK_SIZE);

	if (isFailure)                   // 游戏失败
	{
		setbkmode(TRANSPARENT);      // 文字字体透明
		settextcolor(RGB(255, 0, 0));
		settextstyle(80, 0, _T("宋体"));
		outtextxy(240, 220, _T("游戏失败"));
	}
	FlushBatchDraw();                // 批量绘制
}

void moveSnake()
{
	int i, j;
	for (i = 0; i < HEIGHT; i++)
	{
		for (j = 0; j < WIDTH; j++)
		{
			if (Blocks[i][j] > 0)     // 大于0的为小蛇元素
			{
				Blocks[i][j]++;
			}
		}
	}
	int oldTail_i, oldTail_j, oldHead_i, oldHead_j; // 存储旧蛇
	int max = 0;
	for (i = 0; i < HEIGHT; i++)
	{
		for (j = 0; j < WIDTH; j++)
		{
			if (max < Blocks[i][j])
			{
				max = Blocks[i][j];
				oldTail_i = i;
				oldTail_j = j;
			}
			if (Blocks[i][j] == 2)      // 旧蛇头
			{
				oldHead_i = i;
				oldHead_j = j;
			}
		}
	}
	int newHead_i = oldHead_i;          // 设定变量存储新蛇头
	int newHead_j = oldHead_j;

	if (moveDirection == 'A')           // 根据用户按键,设定新蛇头的位置
	{
		newHead_j = oldHead_j - 1;
	}
	else if (moveDirection == 'D')
	{
		newHead_j = oldHead_j + 1;
	}
	else if (moveDirection == 'W')
	{
		newHead_i = oldHead_i - 1;
	}
	else if (moveDirection == 'S')
	{
		newHead_i = oldHead_i + 1;
	}
	
	if (newHead_i >= HEIGHT || newHead_i < 0 || newHead_j >= WIDTH || newHead_j < 0 || Blocks[newHead_i][newHead_j] > 0) // 失败条件
	{
		isFailure = 1;
		return;
	}

	Blocks[newHead_i][newHead_j] = 1;               // 新蛇头位置数值为1
	if (newHead_i == food_i && newHead_j == food_j) // 如果新蛇头碰到食物
	{
		food_i = rand() % (HEIGHT - 5) + 2;         // 食物重新随机位置
		food_j = rand() % (WIDTH - 5) + 2;
	}
	else
	{
		Blocks[oldTail_i][oldTail_j] = 0;           // 旧蛇尾变成空白
	}
}
void updateWithoutInput()                           // 与输入无关的更新
{
	if (isFailure)
	{
		return;
	}
	static int waitIndex = 1;
	waitIndex++;                                     // 每一帧加1
	if (waitIndex == 10)
	{
		moveSnake();
		waitIndex = 1;
	}
}

void updateWithInput()                                // 和输入有关的更新
{
	if (_kbhit() && isFailure == 0)
	{
		char input = _getch();
		if (input == 'A' || input == 'S' || input == 'D' || input == 'W')
		{
			moveDirection = input;
			moveSnake();
		}
	}
}

int main()
{
	startup();                       // 初始化函数,仅执行一次
	while (1)
	{
		show();                      // 进行绘制
		updateWithoutInput();        // 和输入无关的更新
		updateWithInput();           // 和输入有关的更新
	}
	return 0;
}
  • 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
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/336466
推荐阅读
相关标签
  

闽ICP备14008679号