贪吃蛇小游戏~

在我们写这个小程序之前我们考虑以下的几点:

一、明确需求:

        (1) 只有一条蛇

        (2) 只有一个食物

        (3) 游戏界面,只有边界和部分有墙

        (4) game over  : (1)撞墙  (2)撞自己

        (5)  字符界面

          (6)    每吃一个加分

二、MVC(model:数据结构、view:界面、  controller:控制流)

            1.   M:自顶向下分析

                        蛇:看作链表,用坐标表示

                        食物:坐标

                        墙(已知宽和高)

                        GAME

                我们可以知道需要以下结构体:

                          (1) 坐标

                          (2) 链表

                          (3) 蛇(蛇头、方向)

                          (4) 方向

                          (5) 其他

#pragma once


//坐标原点在左上角
//x是向右   y是向下
typedef struct Position{
	int x;
	int y;
}Position;

//链表结点(单向)
typedef struct Node{
	struct Node *pNext;
	Position data;
}Node;

//方向 (枚举列举所有可能)
typedef enum Direction{
	UP, DOWN, LEFT, RIGHT
}Direction;

//蛇的结构
typedef struct Snake{
	Node *pHead;
	Direction direction;
}Snake;

//游戏用到的其他非独立字段
typedef struct Game{
	Snake snake;
	Position foodPosition;
	int score;
	int sorePerfood;
	int speed;
	int width;	// 宽
	int height;	// 高
}Game;


          2. V   :view 

                 (1)显示墙

#pragma once

#include <windows.h>
#include <stdio.h>
#include "model.h"


void SetPos(int X, int Y)
{
	HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);//获得句柄
	COORD coord;//实现光标的移动
	coord.X = X;
	coord.Y = Y;
	SetConsoleCursorPosition(hStdout, coord); //控制光标
}
//显示外墙
void DisplayWall(int width, int height)
{
	int i;
	//上边
	SetPos(0,0);
	for (i = 0; i < width + 2; i++)
	{
		printf("■");
	}
	//下边
	SetPos(0,height + 1);
	for (i = 0; i < width + 2; i++){	
		printf("■");
	}
	//左边
	for (i = 0; i < height +2 ; i++){
		SetPos(0, i);
		printf("■");
	}
	//右边
	for (i = 0; i < height + 2; i++){
		SetPos(2*(width + 1), i);  //由于用了中文字符,一个中文是二个字符
		printf("■");
	}
	
}

                 (2)显示蛇

//显示蛇的位置
void DisplaySnakeNode(Position pos)
{
	SetPos(2 * (pos.x + 1), pos.y + 1);  //用到了中文字符
	printf("●");
}
//显示蛇
void DisplaySnake(const Snake *pSnake)
{
	Node *pNode = pSnake->phead;
	while (pNode)
	{
		DisplaySnakeNode(pNode->data);//显示蛇的位置
		pNode = pNode->pNext;
	}
}

                 (3)显示食物

//显示食物
void DisplayFood(Position pos)
{
	SetPos(2 * (pos.x + 1), pos.y + 1);  //用了中文字符
	printf("★");
}

我对宽和高做了改动,所以界面比上面的大

     (4)清除数据

//清除数据
void CleanSnakeNode(Position pos)
{
	SetPos(2 * (pos.x + 1), pos.y + 1);  //用了中文字符
	printf(" ");
}

        3.controller   控制流

          游戏的一系列实现

                   (1)对游戏先初始化

        

//蛇的初始化
void SnakeInit(Snake *psnake)
{
	//假定蛇开始长度为3,坐标分别为(5,5)、(6,5)、(7,5)
	//(7,5)->(6,5)->(5,5)
	//采用头插进行初始化
	int i;  
	psnake->pHead= NULL;
	for (i = 0; i < 3; i++)
	{
		Position pos;
		pos.x = i + 5;
		pos.y = 5;

		Node *pNewNode = (Node *)malloc(sizeof(Node));
		assert(pNewNode);
		pNewNode->data = pos;
		pNewNode->pNext = psnake->pHead;
		psnake->pHead = pNewNode;
	}
	psnake->direction = RIGHT;

}

//1重叠  0不重叠
int IsOverlapSnake(int x, int y, const Snake *pSnake)
{
	Node *pNode;
	for (pNode = pSnake->pHead; pNode; pNode = pNode->pNext)
	{
		if (pNode->data.x == x && pNode->data.y == y)
		{
			return 1;
		}
	}
	return 0;
}

//食物的初始化
void FoodInit(Position *pFood, int width, int height, const Snake *pSnake)
{
	int x;
	int y;
	do{
		x = rand() % width;
		y = rand() % height;
	} while (IsOverlapSnake(x, y, pSnake));
	pFood->x = x;
	pFood->y = y;
}
//game的初始化
void GameInit(Game *pGame)
{
	pGame->height =32;
	pGame->width =28;
	system("mode con cols=60 lines=38");

	pGame->score = 0;
	pGame->speed = 200;
	//pGame->scorePerFood= 10;

	SnakeInit(&(pGame->snake));

	//食物(1)随机生成  (2)不能出墙   (3)不能和蛇重合
	FoodInit(&(pGame->foodPosition),
		      pGame->width, pGame->height,
		      &(pGame->snake));
}

                (2)实现字符界面(墙、蛇、食物)

    

	DisplayWall(game.width, game.height);
	DisplaySnake(&(game.snake));
	DisplayFood(game.foodPosition);

                (3)如何走 

                               1.判断蛇不可以180度走

//判断蛇不可以180度转向
		if (GetAsyncKeyState(VK_UP) && game.snake.direction != DOWN) 
		{
			game.snake.direction = UP;
		}
		if (GetAsyncKeyState(VK_DOWN) && game.snake.direction != UP)
		{
			game.snake.direction = DOWN;
		}
		if (GetAsyncKeyState(VK_LEFT) && game.snake.direction != RIGHT)
		{
			game.snake.direction = LEFT;
		}
		if (GetAsyncKeyState(VK_RIGHT) && game.snake.direction != LEFT)
		{
			game.snake.direction = RIGHT;
		}

                     2.一个周期内可能做得事请

//一个周期内可能做得事情
		//1  蛇前进  (1)没吃到食物  添加蛇头结点,删除蛇尾结点
		//           (2)吃到食物    添加蛇头结点,不删除蛇尾,在生成食物
		//   判断下一个前进方向和食物有无重合
		//2  游戏结束   (1)撞墙   (2)撞自己
		Position NextPos = GetNextPosition(&(game.snake)); //得到下一个前进的位置
		//进行判断
		if (NextPos.x == game.foodPosition.x && NextPos.y == game.foodPosition.y)
		{
			//吃到食物
			PushFront(&(game.snake),NextPos); //添加蛇头

			game.score += game.sorePerfood;  //得分

			FoodInit(&(game.foodPosition), game.width, game.height, &(game.snake));//生成食物

			DisplayFood(game.foodPosition);
		}
		else{
			//没吃到食物
			PushFront(&(game.snake), NextPos); //添加蛇头
			PopBack(&(game.snake));  //删除蛇尾
		}
		if (IsCashWall(game.width, game.height, NextPos))
		{
			break;
		}
		if (IsCashhimself(&(game.snake)))
		{
			break;
		}
		Sleep(game.speed);
	}

所有代码如下~

main.c

#include <windows.h>
#include "controller.h"
#include "view.h"

int main()
{
	printf("欢迎\n");
	system("pause");
	system("cls");

	
	printf(" 欢迎\n");
	system("pause");
	system("cls");

	
	printf("热烈欢迎\n");
	system("pause");
	system("cls");
	GameStart();

	system("pause");
	return 0;
}

view.h

#pragma once

#include <windows.h>
#include <stdio.h>
#include "model.h"

void SetPos(int X, int Y)
{
	HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);//获得句柄
	COORD coord;//实现光标的移动
	coord.X = X;
	coord.Y = Y;
	SetConsoleCursorPosition(hStdout, coord); //控制光标
}
//显示外墙
void DisplayWall(int width, int height)
{
	int i;
	//上边
	SetPos(0,0);
	for (i = 0; i < width + 2; i++)
	{
		printf("■");
	}
	//下边
	SetPos(0,height + 1);
	for (i = 0; i < width + 2; i++){	
		printf("■");
	}
	//左边
	for (i = 0; i < height +2 ; i++){
		SetPos(0, i);
		printf("■");
	}
	//右边
	for (i = 0; i < height + 2; i++){
		SetPos(2*(width + 1), i);
		printf("■");
	}
}

//显示蛇的位置
void DisplaySnakeNode(Position pos)
{
	SetPos(2 * (pos.x + 1), pos.y + 1);  //用到了中文字符
	printf("●");
}
//显示蛇
void DisplaySnake(const Snake *pSnake)
{
	Node *pNode = pSnake->pHead;
	while (pNode)
	{
		DisplaySnakeNode(pNode->data);//显示蛇的位置
		pNode = pNode->pNext;
	}
}


//显示食物
void DisplayFood(Position pos)
{
	SetPos(2 * (pos.x + 1), pos.y + 1);  //用了中文字符
	printf("★");
}

//清除数据
void CleanSnakeNode(Position pos)
{
	SetPos(2 * (pos.x + 1), pos.y + 1);  //用了中文字符
	printf("  ");
}

model.h

#pragma once


//坐标原点在左上角
//x是向右   y是向下
typedef struct Position{
	int x;
	int y;
}Position;

//链表结点(单向)
typedef struct Node{
	struct Node *pNext;
	Position data;
}Node;

//方向 (枚举列举所有可能)
typedef enum Direction{
	UP, DOWN, LEFT, RIGHT
}Direction;

//蛇的结构
typedef struct Snake{
	Node *pHead;
	Direction direction;
}Snake;

//游戏用到的其他非独立字段
typedef struct Game{
	Snake snake;
	Position foodPosition;
	int score;
	int sorePerfood;
	int speed;
	int width;	// 宽
	int height;	// 高
}Game;


controller.h

#pragma once

#include "model.h"
#include "view.h"
#include <windows.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>


//蛇的初始化
void SnakeInit(Snake *psnake)
{
	//假定蛇开始长度为3,坐标分别为(5,5)、(6,5)、(7,5)
	//(7,5)->(6,5)->(5,5)
	//采用头插进行初始化
	int i;  
	psnake->pHead= NULL;
	for (i = 0; i < 3; i++)
	{
		Position pos;
		pos.x = i + 5;
		pos.y = 5;

		Node *pNewNode = (Node *)malloc(sizeof(Node));
		assert(pNewNode);
		pNewNode->data = pos;
		pNewNode->pNext = psnake->pHead;
		psnake->pHead = pNewNode;
	}
	psnake->direction = RIGHT;

}

//1重叠  0不重叠
int IsOverlapSnake(int x, int y, const Snake *pSnake)
{
	Node *pNode;
	for (pNode = pSnake->pHead; pNode; pNode = pNode->pNext)
	{
		if (pNode->data.x == x && pNode->data.y == y)
		{
			return 1;
		}
	}
	return 0;
}

//食物的初始化
void FoodInit(Position *pFood, int width, int height, const Snake *pSnake)
{
	int x;
	int y;
	do{
		x = rand() % width;
		y = rand() % height;
	} while (IsOverlapSnake(x, y, pSnake));
	pFood->x = x;
	pFood->y = y;
}
//game的初始化
void GameInit(Game *pGame)
{
	pGame->height =32;
	pGame->width =28;
	system("mode con cols=60 lines=38");

	pGame->score = 0;
	pGame->speed = 200;
	//pGame->scorePerFood= 10;

	SnakeInit(&(pGame->snake));

	//食物(1)随机生成  (2)不能出墙   (3)不能和蛇重合
	FoodInit(&(pGame->foodPosition),
		      pGame->width, pGame->height,
		      &(pGame->snake));
}

//得到蛇的下一个前进方向
Position GetNextPosition(const Snake *pSnake)
{
	Position nextPos;
	nextPos.x = pSnake->pHead->data.x;
	nextPos.y = pSnake->pHead->data.y;
	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;
}


//添加蛇头(头插)
void PushFront(Snake *pSnake, Position NextPos)
{
	Node *pNewNode = (Node *)malloc(sizeof(Node));
	assert(pNewNode);

	pNewNode->data = NextPos;
	pNewNode->pNext = pSnake->pHead;
	pSnake->pHead= pNewNode;
	
	DisplaySnakeNode(NextPos);
}

//删除蛇尾(尾删)
void PopBack(Snake *pSnake)  //删除蛇尾
{
	Node *pNode ;
	for (pNode = pSnake->pHead; pNode->pNext->pNext != NULL; pNode = pNode->pNext)
	{}
	//找到了倒数第二个结点
	CleanSnakeNode(pNode->pNext->data);//清楚掉倒数第一个

	free(pNode->pNext);
	pNode->pNext = NULL;
}

//撞墙  1撞了   0没有
int IsCashWall(int width, int height,Position NextPos)
{
	if (NextPos.x <0 || NextPos.x >= width )
	{
		return 1;
	}
	if (NextPos.y < 0 || NextPos.y>= height)
	{
		return 1;
	}
	return 0;
}

//撞自己   1撞了   0没有
int  IsCashhimself(const Snake *pNake)
{
	Node *pHead = pNake->pHead;
	Node *pNode = pNake->pHead->pNext;
	while (pNode!=NULL)
	{
		if (pHead->data.x == pNode->data.x && pHead->data.y == pNode->data.y)
		{
			return 1;
		}
		pNode = pNode->pNext;
	}
	return 0;
}

void GameStart()
{
	Game game;
	GameInit(&game);
	DisplayWall(game.width, game.height);
	DisplaySnake(&(game.snake));
	DisplayFood(game.foodPosition);

	while (1)
	{
		//判断蛇不可以180度转向
		if (GetAsyncKeyState(VK_UP) && game.snake.direction != DOWN) 
		{
			game.snake.direction = UP;
		}
		if (GetAsyncKeyState(VK_DOWN) && game.snake.direction != UP)
		{
			game.snake.direction = DOWN;
		}
		if (GetAsyncKeyState(VK_LEFT) && game.snake.direction != RIGHT)
		{
			game.snake.direction = LEFT;
		}
		if (GetAsyncKeyState(VK_RIGHT) && game.snake.direction != LEFT)
		{
			game.snake.direction = RIGHT;
		}
		//一个周期内可能做得事情
		//1  蛇前进  (1)没吃到食物  添加蛇头结点,删除蛇尾结点
		//           (2)吃到食物    添加蛇头结点,不删除蛇尾,在生成食物
		//   判断下一个前进方向和食物有无重合
		//2  游戏结束   (1)撞墙   (2)撞自己
		Position NextPos = GetNextPosition(&(game.snake)); //得到下一个前进的位置
		//进行判断
		if (NextPos.x == game.foodPosition.x && NextPos.y == game.foodPosition.y)
		{
			//吃到食物
			PushFront(&(game.snake),NextPos); //添加蛇头

			game.score += game.sorePerfood;  //得分

			FoodInit(&(game.foodPosition), game.width, game.height, &(game.snake));//生成食物

			DisplayFood(game.foodPosition);
		}
		else{
			//没吃到食物
			PushFront(&(game.snake), NextPos); //添加蛇头
			PopBack(&(game.snake));  //删除蛇尾
		}
		if (IsCashWall(game.width, game.height, NextPos))
		{
			break;
		}
		if (IsCashhimself(&(game.snake)))
		{
			break;
		}
		Sleep(game.speed);
	}
	

}


运行结果:



         得分项还没有写出来,后续完善~





猜你喜欢

转载自blog.csdn.net/z_juan1/article/details/80954516