C++贪吃蛇游戏开发实践

C++贪吃蛇游戏开发实践

snake

对象分析

snakemove

我们首先需要确定一个像素点组成的地图(画布),要确定行数、列数和像素点大小。这个地图上将会有两个对象:蛇和食物。

蛇由头和身子组成,他们都有自己的位置,所以考虑使用位置点数组来存储。同时,还要存储蛇头方向和蛇身体节数。

而食物只要记录其位置即可。

地图上的内容就是蛇与食物,如果做图形界面,就是不断地在画布上绘制蛇与食物的过程。这里主要用到了绘制矩形与填充矩形的函数。

蛇的移动

蛇的移动就是对蛇身数组的操作,可以分类讨论。如果是蛇身,我们从蛇尾开始观察,会发现每一节的位置将是前一节的位置。即前一节的位置直接赋值给后一节即可,但要注意要从蛇尾开始操作。如果是蛇头,将取决于当前蛇的方向,但要注意不能掉头。

snakemove

蛇吃食物

蛇吃食物的判定是蛇头与食物位置是否重叠,如果吃到了要增长蛇身和重新投放食物。增长蛇身其实就是在蛇身数组的末尾再复制一份蛇尾。重新投放食物时要注意不能放在蛇身上。

eat

算法实现

图形界面

图形界面的代码在主函数里:

initgraph(WIDTH, HEIGHT);
while (true) {
    
    
    ...
}
closegraph();

整个绘制对象只用到了绘制矩形

  • setcolor(color);设置线颜色
  • setfillcolor(color);设置填充颜色
  • fillrect(x1, y1, x2, y2);绘制矩形需要左上角和右下角的点位置

方向枚举

enum Direction
{
    
    
	RIGHT = 39,
	LEFT = 37,
	DOWN = 40,
	UP = 38,
};

为了方便与键盘输入对应,将上下左右的键值进行枚举。

小蛇移动

结合上面的示意图理解小蛇移动的算法实现。

void snakeMove()
{
    
    
	// 更新蛇身
	for (int i = snake.num-1; i > 0; i--) {
    
    
		snake.body[i].x = snake.body[i-1].x;
		snake.body[i].y = snake.body[i-1].y;
	}
	// 更新蛇头
	switch (snake.dir) {
    
    
	case RIGHT:
		snake.body[0].x++;
		break;
	case LEFT:
		snake.body[0].x--;
		break;
	case DOWN:
		snake.body[0].y++;
		break;
	case UP:
		snake.body[0].y--;
		break;
	}
}

蛇吃食物

结合上面的示意图理解蛇吃食物的算法实现。

void eatFood()
{
    
    
	int x = snake.body[0].x;
	int y = snake.body[0].y;
	if (food.pos.x == x && food.pos.y == y) {
    
    
		// 食物重新投放
		bool flag;
		do {
    
    
			flag = false;
			food.pos.x = rand()%(M-2) + 1;
			food.pos.y = rand()%(N-2) + 1;
			for (int i = 0; i < snake.num; i++) {
    
    
				int x = snake.body[i].x, y = snake.body[i].y;
				if (x == food.pos.x && y == food.pos.y) {
    
    
					flag = true;
					break;
				}
			}
		} while (flag);
		// 蛇身增长
		snake.body[snake.num].x = snake.body[snake.num-1].x;
		snake.body[snake.num].y = snake.body[snake.num-1].y;
		snake.num++;
	}
}

代码实现

这里使用的是EGE库,需要提前安装。

#include <stdio.h>
#include <stdlib.h>
#include <graphics.h>
// 画布参数
const int N = 10;	// 行数
const int M = 10;	// 列数
const int L = 40;	// 像素点大小
const int T = 400;	// 间隔
// 方向枚举
enum Direction
{
    
    
	RIGHT = 39,
	LEFT = 37,
	DOWN = 40,
	UP = 38,
};
// 点结构体
struct Point
{
    
    
	int x, y;
	int X() {
    
    return x*L;}
	int Y() {
    
    return y*L;}
};
// 蛇结构体
struct Snake
{
    
    
	int num;
	Point body[N*M];
	Direction dir;
} snake;
// 食物结构体
struct Food
{
    
    
	Point pos;
} food;
// 功能函数
void initSnake();	// 初始化小蛇
void initFood();	// 初始化食物
void drawBoard();	// 绘制网格
void drawSnake();	// 绘制小蛇
void drawFood();	// 绘制食物
void drawAll();		// 全部绘制
void snakeMove();	// 小蛇移动
void eatFood();		// 吃到食物
bool snakeDie();	// 小蛇撞死
void keyDown();		// 按键处理

// -------------------主函数----------------------
int main()
{
    
    
	initgraph(M*L, N*L);
	begin:
	initSnake();
	initFood();
	while (true) {
    
    
		if (kbhit()) {
    
    	// 监听键盘按键
			keyDown();
		}
		snakeMove();
		if (snakeDie()) {
    
    
			MessageBox(getHWnd(),"Game Over!","rePlay",MB_OK);
			goto begin;
		}
		eatFood();
		drawAll();
		Sleep(T);
	}
	closegraph();
	return 0;
}

void initSnake()
{
    
    
	snake.num = 3;
	snake.dir = RIGHT;
	snake.body[2].x = 1;
	snake.body[2].y = 1;
	snake.body[1].x = 2;
	snake.body[1].y = 1;
	snake.body[0].x = 3;
	snake.body[0].y = 1;
}

void initFood()
{
    
    
	bool flag;
	do {
    
    
		flag = false;
		food.pos.x = rand()%(M-2) + 1;
		food.pos.y = rand()%(N-2) + 1;
		// 如果食物在蛇身上则重新投放
		for (int i = 0; i < snake.num; i++) {
    
    
			int x = snake.body[i].x, y = snake.body[i].y;
			if (x == food.pos.x && y == food.pos.y) {
    
    
				flag = true;
				break;
			}
		}
	} while (flag);
}

void drawBoard()
{
    
    
	for (int i = 0; i < M; i++) {
    
    
		for (int j = 0; j < N; j++) {
    
    
			setcolor(BLUE);
			setfillcolor(BLACK);
			fillrect(i*L, j*L, i*L+L, j*L+L);
		}
	}
	for (int i = 0; i < N; i++) {
    
    
		setcolor(BLUE);
		setfillcolor(EGERGB(0, 0, 139));
		fillrect(0, i*L, L, i*L+L);
		fillrect((M-1)*L, i*L, M*L, i*L+L);
	}
	for (int i = 0; i < M; i++) {
    
    
		setcolor(BLUE);
		setfillcolor(EGERGB(0, 0, 139));
		fillrect(i*L, 0, i*L+L, L);
		fillrect(i*L, (N-1)*L, i*L+L, N*L);
	}
}

void drawSnake()
{
    
    
	for (int i = 0; i < snake.num; i++) {
    
    
		setcolor(BLUE);
		setfillcolor(EGERGB(0,100,0));
		fillrect(snake.body[i].X(),snake.body[i].Y(),snake.body[i].X()+L,snake.body[i].Y()+L);
	}
}

void drawFood()
{
    
    
	setcolor(BLUE);
	setfillcolor(RED);
	fillrect(food.pos.X(),food.pos.Y(),food.pos.X()+L,food.pos.Y()+L);
}

void drawAll()
{
    
    
	drawBoard();
	drawFood();
	drawSnake();
}

void snakeMove()
{
    
    
	// 更新蛇身
	for (int i = snake.num-1; i > 0; i--) {
    
    
		snake.body[i].x = snake.body[i-1].x;
		snake.body[i].y = snake.body[i-1].y;
	}
	// 更新蛇头
	switch (snake.dir) {
    
    
	case RIGHT:
		snake.body[0].x++;
		break;
	case LEFT:
		snake.body[0].x--;
		break;
	case DOWN:
		snake.body[0].y++;
		break;
	case UP:
		snake.body[0].y--;
		break;
	}
}

void eatFood()
{
    
    
	int x = snake.body[0].x;
	int y = snake.body[0].y;
	if (food.pos.x == x && food.pos.y == y) {
    
    
		// 食物重新投放
		bool flag;
		do {
    
    
			flag = false;
			food.pos.x = rand()%(M-2) + 1;
			food.pos.y = rand()%(N-2) + 1;
			for (int i = 0; i < snake.num; i++) {
    
    
				int x = snake.body[i].x, y = snake.body[i].y;
				if (x == food.pos.x && y == food.pos.y) {
    
    
					flag = true;
					break;
				}
			}
		} while (flag);
		// 蛇身增长
		snake.body[snake.num].x = snake.body[snake.num-1].x;
		snake.body[snake.num].y = snake.body[snake.num-1].y;
		snake.num++;
	}
}

bool snakeDie()
{
    
    
	int x = snake.body[0].x;
	int y = snake.body[0].y;
	// 超出边框
	if (x >= (M-1) || y >= (N-1) || x <= 0 || y <= 0) {
    
    
		return true;
	}
	// 迟到自身
	for (int i = 1; i < snake.num; i++) {
    
    
		if (x == snake.body[i].x && y == snake.body[i].y) {
    
    
			return true;
		}
	}
	return false;
}

void keyDown()
{
    
    
	char userKey = 0;
	userKey = getch();
	switch (userKey) {
    
    
	case LEFT:
		if (snake.dir != RIGHT) {
    
    
			snake.dir = LEFT;
		}
		break;
	case RIGHT:
		if (snake.dir != LEFT) {
    
    
			snake.dir = RIGHT;
		}
		break;
	case UP:
		if (snake.dir != DOWN) {
    
    
			snake.dir = UP;
		}
		break;
	case DOWN:
		if (snake.dir != UP) {
    
    
			snake.dir = DOWN;
		}
	}
}

猜你喜欢

转载自blog.csdn.net/qq_22328011/article/details/128440997