博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
贪吃蛇
阅读量:2242 次
发布时间:2019-05-09

本文共 6665 字,大约阅读时间需要 22 分钟。

很多人都玩过贪吃蛇这个游戏,今天我们就来简单完成一个贪吃蛇的小游戏。首先,贪吃蛇有以下几个要求:

  1. 只有一条蛇
  2. 每次只出现一个食物
  3. 蛇吃到食物长度会加1
  4. 蛇撞到墙或者撞到自己,游戏结束

一般情况下,写一些项目,会经常用到一种思想,MVC——Model(模型)-View(视图)-Controller(控制器),在Model里面通常会写一些游戏的所包含的各种事物的设计,例如:用结构体来表示蛇,食物······Model里面的基本都是只有游戏设计者才能看见的东西。而View里面主要存放显示视图的代码,也就是我们玩游戏的界面以及玩家所能看见的东西,最后Controller主要存放游戏的主体代码,也就是一个玩游戏的过程。

Model

我们先从Model开始,我们需要在游戏里面准备哪些东西?

蛇应该用二维数组表示还是链表表示,如果用二维数组,那么每一次蛇的移动都要从新规划坐标,会特别的麻烦,如果我们用链表,我们完全可以在链表的每个节点里面存放坐标,这样蛇一动起来就会比较容易。另外,蛇的前进方向也需要保存,不然如何移动。

由此,需要一个坐标的结构体,一个蛇的结构体,一个蛇前进方向的枚举,链表节点的结构体,还要一个游戏的结构体(用来存放墙的位置,宽度,高度,食物,分数,速度)

//坐标结构体	typedef struct Position	{		int x;		int y;	}Position;		//蛇前进方向枚举	typedef enum Direction	{		UP,RIGHT,DOWN,LEFT	}Direction;		//链表结点的结构体	typedef struct Node	{		Position pos;		struct Node *next;	}Node;	//蛇本身	typedef struct Snake	{		Node *head;		Direction direction;	}Snake;	//游戏	typedef struct Game	{		Snake snake;		Position food;		int score;		int speed;		int width;		int height;	}Game;

然后就要开始初始化游戏的各项内容,有初始化就要有销毁(针对不同的情况)

//蛇的初始化	void SnakeInit(Snake *pSnake)	{		assert(pSnake);		pSnake->head = NULL;		pSnake->direction = RIGHT;		for (int i = 0; i < 3; i++)		{			Node *newNode = (Node *)malloc(sizeof(Node));			newNode->pos.x = 3 + i;			newNode->pos.y = 3;			newNode->next = pSnake->head;			pSnake->head = newNode;		}	}		bool IsOverSnake(Snake *pSnake, int x, int y)	{		for (Node *cur = pSnake->head; cur != NULL; cur = cur->next)		{			if (cur->pos.x == y && cur->pos.y == y)			{				return true;			}		}		return false;	}	//食物初始化(以后会用到,因为每一次都要更新食物)	void FoodInit(Position *pFood, int width, int height, Snake *pSnake)	{		assert(pFood);		assert(pSnake);		int x = 0;		int y = 0;		do		{			x = rand() % width;			y = rand() % height;		} while (IsOverSnake(pSnake, x, y));		pFood->x = x;		pFood->y = y;		DisplayFood(x, y);	}	//游戏初始化	void GameInit(Game *pGame)	{		assert(pGame);		pGame->score = 0;		pGame->width = 28;		pGame->height = 27;		pGame->speed = 300;		SnakeInit(&pGame->snake);		FoodInit(&pGame->food, pGame->width, pGame->height, &pGame->snake);	}	//蛇的销毁	void SnakeDestroy(Snake *pSnake)	{		assert(pSnake);		Node *cur, *next;		for (cur = pSnake->head; cur != NULL; cur = next)		{			next = cur->next;			free(cur);		}	}	//游戏销毁	void GameDestroy(Game *pGame)	{		assert(pGame);		SnakeDestroy(&pGame->snake);	}

蛇又是如何移动的?我们可以将蛇看做去尾添头,就是把尾巴去掉,再在头解一个节点,蛇就移动了

//添头	void AddSnakeHead(Snake *pSnake, Position nextPos)	{		assert(pSnake);		Node *newNode = (Node *)malloc(sizeof(Node));		newNode->pos.x = nextPos.x;		newNode->pos.y = nextPos.y;		newNode->next = pSnake->head;		pSnake->head = newNode;		DisplaySnake(pSnake); //这个函数在View里面	}	//去尾	void RemoveSnakeTail(Snake *pSnake)	{		assert(pSnake);		Node *cur;		for (cur = pSnake->head; cur->next->next != NULL; cur = cur->next)		{			;		}		CleanSnakeBlock(cur->next->pos.x, cur->next->pos.y);//这个函数也在View里面		free(cur->next);		cur->next = NULL;	}

View

我们需要在视图里面展示墙、蛇、食物、分数

//显示墙	void DisplayWall(int width, int height)	{		SetCurPos(0, 0);		for (int i = 0; i < width + 2; i++)		{			printf("▇");		}		SetCurPos(0, height + 1);		for (int i = 0; i < width + 2; i++)		{			printf("▇");		}		for (int i = 0; i < height + 2; i++)		{			SetCurPos(0, i);			printf("▇");		}		for (int i = 0; i < height + 2; i++)		{			SetCurPos(2 * (width + 1), i);			printf("▇");		}	}	//显示蛇	//下面代码中坐标会*2是因为食物以及蛇本身用到的"▇"是一个中文符号,占两个字节,也就是两个光标位置	void DisplaySnake(Snake *pSnake)	{		assert(pSnake);		for (Node *cur = pSnake->head; cur != NULL; cur = cur->next)		{			SetCurPos(2 * (cur->pos.x + 1), cur->pos.y + 1);			if (cur == pSnake->head)			{				printf("⊙");			}			else			{				printf("▇");			}		}	}	//显示食物	void DisplayFood(int x, int y)	{		SetCurPos(2 * (x + 1), y + 1);		printf("▇");	}	//显示分数	void DisplayScore(int score)	{		SetCurPos(30 * 2 + 10, 10);		printf("得分: %d", score);	}

上面函数中有一个SetCurPos的函数,是用来设置光标的位置,然后接下来的内容就会在光标位置处显示

下面代码中用到了几个Windows的函数

void SetCurPos(int x, int y)	{		HANDLE hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);		COORD coord = { x, y };		SetConsoleCursorPosition(hStdOutput, coord);	}	//隐藏光标	void ViewInit(int width, int height)	{		HANDLE hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);		CONSOLE_CURSOR_INFO info;		GetConsoleCursorInfo(hStdOutput, &info);		info.bVisible = 0;		SetConsoleCursorInfo(hStdOutput, &info);	}

Controller

static void Pause()	{		while (1)		{			Sleep(300);			if (GetAsyncKeyState(VK_SPACE))			{				break;			}		}	}		static Position GetNextPosition(Snake *pSnake)	{		Position nextPos = pSnake->head->pos;		switch (pSnake->direction)		{		case UP:			nextPos.y -= 1;			break;		case DOWN:			nextPos.y += 1;			break;		case LEFT:			nextPos.x -= 1;			break;		case RIGHT:			nextPos.x += 1;			break;		}		return nextPos;	}	//判断食物是否被吃	static bool IsEaten(Position nextPos, Position food)	{		return nextPos.x == food.x && nextPos.y == food.y;	}	//判断是否撞到墙	static bool IsHitWall(Position nextPos, int width, int height)	{		if (nextPos.x < 0)		{			return true;		}		if (nextPos.x >= width)		{			return true;		}		if (nextPos.y < 0)		{			return true;		}		if (nextPos.y >= height)		{			return true;		}		return false;	}	//判断是否撞到自己	static bool IsHitSelf(Position nextPos, Snake *pSnake)	{		for (Node *cur = pSnake->head->next; cur != NULL; cur = cur->next)		{			if (cur->pos.x == nextPos.x && cur->pos.y == nextPos.y)			{				return true;			}		}		return false;	}		void test()	{		Game game;		GameInit(&game);		ViewInit(game.width, game.height);		DisplayWall(game.width, game.height);		DisplaySnake(&game.snake);		DisplayScore(game.score);		SetCurPos(30 * 2 + 10, 7);		printf("上下左右按键表示移动,SPACE表示暂停,F1加速");		while (1)		{			if (GetAsyncKeyState(VK_UP) && game.snake.direction != DOWN)			{				game.snake.direction = UP;			}			else if (GetAsyncKeyState(VK_DOWN) && game.snake.direction != UP)			{				game.snake.direction = DOWN;			}			else if (GetAsyncKeyState(VK_LEFT) && game.snake.direction != RIGHT)			{				game.snake.direction = LEFT;			}			else if (GetAsyncKeyState(VK_RIGHT) && game.snake.direction != LEFT)			{				game.snake.direction = RIGHT;			}			else if (GetAsyncKeyState(VK_SPACE))			{				Pause();			}			else if (GetAsyncKeyState(VK_F1)) {				game.speed = 100;			}			Position nextPos = GetNextPosition(&game.snake);			if (IsEaten(nextPos, game.food))			{				AddSnakeHead(&game.snake, nextPos);				game.score += 10;				if (game.speed >= 100)				{					game.speed -= 20;				}				DisplayScore(game.score);				FoodInit(&game.food, game.width, game.height, &game.snake);			}			else			{				RemoveSnakeTail(&game.snake);				AddSnakeHead(&game.snake, nextPos);			}			if (IsHitWall(nextPos, game.width, game.height))			{				break;			}			if (IsHitSelf(nextPos, &game.snake))			{				break;			}			Sleep(game.speed);		}		GameDestroy(&game);	}

上述代码中GetAsyncKeyState()函数用于获取按键信息(上下左右按键)。

上述代码不完整,附完整代码链接

转载地址:http://ddwdb.baihongyu.com/

你可能感兴趣的文章
【selenium】selenium ide的安装过程
查看>>
【手机自动化测试】monkey测试
查看>>
【英语】软件开发常用英语词汇
查看>>
Fiddler 抓包工具总结
查看>>
【雅思】雅思需要购买和准备的学习资料
查看>>
【雅思】雅思写作作业(1)
查看>>
【雅思】【大作文】【审题作业】关于同不同意的审题作业(重点)
查看>>
【Loadrunner】通过loadrunner录制时候有事件但是白页无法出来登录页怎么办?
查看>>
【English】【托业】【四六级】写译高频词汇
查看>>
【托业】【新东方全真模拟】01~02-----P5~6
查看>>
【托业】【新东方全真模拟】03~04-----P5~6
查看>>
【托业】【新东方托业全真模拟】TEST05~06-----P5~6
查看>>
【托业】【新东方托业全真模拟】TEST09~10-----P5~6
查看>>
【托业】【新东方托业全真模拟】TEST07~08-----P5~6
查看>>
solver及其配置
查看>>
JAVA多线程之volatile 与 synchronized 的比较
查看>>
Java集合框架知识梳理
查看>>
笔试题(一)—— java基础
查看>>
Redis学习笔记(三)—— 使用redis客户端连接windows和linux下的redis并解决无法连接redis的问题
查看>>
Intellij IDEA使用(一)—— 安装Intellij IDEA(ideaIU-2017.2.3)并完成Intellij IDEA的简单配置
查看>>