c++从入门到精通——贪吃蛇

贪吃蛇

该游戏通过控制蛇头方向吃蛋,从而使得蛇变得越来越长,速度越快。如下图。

* * * * * * * * * * * * * * * * * * * @ * * * * * *
*                                     =           *
*                                     =           *
*                                     =           *
*                                 #   =           *
*                       = = = = = = = =           * create by zt
*                                                 * a : left
*                                                 * d : right
*                                                 * w : up
*                                                 * s : down
*                                                 *
*                                                 *
* * * * * * * * * * * * * * * * * * * * * * * * * *
得分:1000分
GAME OVER!!!
请按任意键继续. . .

游戏元素

​ 游戏中元素分为:墙壁、蛇、食物以及蛇的可行区域和右侧的版本号和游戏玩法提示

墙壁

* 星号表示,代表一个区域范围,也就是蛇的可移动区域,蛇如果碰到墙壁视为死亡,也就是GameOver!

​ 分为蛇头 蛇身,蛇头用@符号表示,蛇身用 = 等号表示,当蛇吃到食物时候,蛇身+1,意味着身长度变成。贪食蛇可以通过不断地吃食物来增加自己的身体

食物:

​ #井号表示,蛇碰到食物会将食物吃掉

可移动区域

​ 空格 表示,代表蛇可以移动的区域

提示信息

​ 右侧展示,可以显示当前贪食蛇版本号、制作人员、游戏玩法等提示信息!

游戏规则

​ 当运行起游戏时候,游戏画面是静止不动的,可以默认如上图中,蛇头朝右,游戏中设置了 w s a d 4个按键分别代表,上、下、左、右,也是用户比较常用的方向按键,当用户输入 w或者s或者d时候激活游戏,注意输入a不可以激活,因为蛇不可以180°转弯,因此蛇的移动方向只可以一直向前或者90°旋转。

​ 当蛇吃掉食物时候,此时蛇会增加一个身段,另外食物需要重新随机的设置到屏幕上。

​ 游戏结束方式有两种:一、蛇碰到墙壁视为死亡;二、蛇头碰到蛇身子,把自己吃掉也视为死亡。

实现思路

利用Excel模拟游戏

在这里插入图片描述
我们实现游戏,并非直接上代码,而是要先分清楚游戏的需求、玩法、和游戏中各个元素的作用以及游戏规则后才可以考虑动手
在没有任何成品的情况下,我们可以先通过画图或者电脑上PS或者有相关的游戏策划师,游戏美术人员提供的图片来分析游戏
在本案例中,我们可以先通过一个Excel表来分析游戏,因为恰巧Excel也是网格的,和我们的游戏和类似,其他游戏就需要根据不同的类型来用不同的图片模拟游戏了。
在Excel表中我们可以看到,模拟的游戏分为了横向和纵向,游戏内部可以看成一个二维数组,我们只是不断的去修改二维数组的内容,然后显示到屏幕上即可。

游戏搭建

很明显游戏分为两种状态,第一种就是静止态,也就是进入游戏时候的展示画面,在这个画面下,所有游戏元素都是静止不动的,二另一种就是激活游戏后开始移动游戏的状态

游戏移动

当激活游戏后,也分为两种,一种是死亡,这个是当蛇头碰到蛇身或者是碰到墙壁者两种死亡,这个我们暂时先不考虑,第二种是正常移动,那么我们先分析下正常移动
在正常移动时候,也分为两种状态
第一种:蛇没有吃的食物
这个时候,蛇只是单纯的移动,比如上述的蛇向前移动后,应该到对应的第五行第7列的位置,离食物很近了,但是还没有吃到,这时候,蛇应该更新蛇头的位置,并且将之前蛇尾巴的位置置为空格,也就是表示向前移动。
在这里插入图片描述
第二种:蛇吃到食物
如上图,这时候如果继续向右行走,蛇将吃到食物,当吃到食物时,当前的蛇头的位置应该为之前食物的位置 ,那么蛇尾由于吃到了食物,就还是在原有位置,然后食物再重新分配到一个其他的位置,这个位置不能是蛇、也不能是墙
在这里插入图片描述

代码

food.cpp
food.h
game.cpp
snak.cpp
snak.h
snake.cpp
snake.h
wall.cpp
wall.h
贪食蛇游戏.cpp

wall.h文件

#ifndef _WALL_HEAD
#define _WALL_HEAD
//#pragma  once

#include <iostream>
using namespace std;

class Wall
{
public:
	enum {
		ROW = 26,
		COL = 26
	};

	
	//初始化墙壁
	void initWall();

	//画出墙壁
	void drawWall();

	//根据索引设置 二维数组里的内容
	void setWall(int x, int y, char c);

	//根据索引获取当前位置的符号
	char getWall(int x, int y);

private:
	char gameArray[ROW][COL];
};

#endif

墙cpp文件

#include "wall.h"


void Wall::initWall()
{
	for (int i = 0; i < ROW;i++)
	{
		for (int j = 0; j < COL;j++)
		{
			//放墙壁
			if (i == 0  || j == 0 || i == ROW - 1 || j == COL - 1)
			{
				gameArray[i][j] = '*';
			}
			else
			{
				gameArray[i][j] = ' ';
			}
		}
	}


}

void Wall::drawWall()
{
	for (int i = 0; i < ROW;i ++)
	{
		for (int j = 0; j < COL; j ++)
		{
			cout << gameArray[i][j] << " ";
		}
		if (i == 5)
		{
			cout << "create by zt";
		}
		if (i == 6)
		{
			cout << "a : left";
		}
		if (i == 7)
		{
			cout << "d : right";
		}
		if (i == 8)
		{
			cout << "w : up";
		}
		if (i == 9)
		{
			cout << "s : down";
		}
		cout << endl;
	}


}

void Wall::setWall(int x, int y, char c)
{
	gameArray[x][y] = c;
}

char Wall::getWall(int x, int y)
{
	return gameArray[x][y];
}

#pragma  once
#include <iostream>
#include "wall.h"
#include "food.h"
using namespace std;

class Snake
{
public:
	Snake(Wall & tempWall, Food & food );

	enum { UP = 'w', DOWN = 's', LEFT = 'a' , RIGHT = 'd'};

	//节点
	struct Point
	{
		//数据域
		int x;
		int y;
		//指针域
		Point * next;
	};

	//初始化蛇
	void initSnake();

	// 销毁节点
	void destroyPoint();

	// 添加节点
	void addPoint(int x,int y);


	// 删除节点
	void delPoint();

	//移动蛇操作
	//返回值代表 移动是否成功
	bool move(char key);


	//设定难度
	//获取刷屏时间
	int getSleepTime();
	//获取蛇身段
	int countList();

	//获取分数
	int getScore();

	Point * pHead;

	Wall & wall;

	Food & food;

	bool isRool; //判断循环标示
};



蛇cpp文件

#include "snake.h"
#include <windows.h>
void gotoxy1(HANDLE hOut1, int x, int y)
{
	COORD pos;
	pos.X = x;             //横坐标
	pos.Y = y;            //纵坐标
	SetConsoleCursorPosition(hOut1, pos);
}
HANDLE hOut1 = GetStdHandle(STD_OUTPUT_HANDLE);//定义显示器句柄变量


Snake::Snake(Wall & tempWall, Food & tmpFood) : wall(tempWall), food(tmpFood)
{
	pHead = NULL;
	isRool = false;
}


void Snake::initSnake()
{
	destroyPoint();

	addPoint(5, 3);
	addPoint(5, 4);
	addPoint(5, 5);
}

//销毁所有节点
void Snake::destroyPoint()
{
	Point * pCur = pHead;

	while (pHead != NULL)
	{
		pCur = pHead->next;
		delete pHead;

		pHead = pCur;
	}
}

void Snake::addPoint(int x, int y)
{
	//创建新节点
	Point * newPoint = new Point;
	newPoint->x = x;
	newPoint->y = y;
	newPoint->next = NULL;

	//如果原来头不为空 改为 身子

	if (pHead != NULL)
	{
		wall.setWall(pHead->x, pHead->y, '=');

		gotoxy1(hOut1, pHead->y * 2, pHead->x);
		cout << "=";

	}

	newPoint->next = pHead;

	pHead = newPoint; //更新头部

	wall.setWall(pHead->x, pHead->y, '@');
	gotoxy1(hOut1, pHead->y * 2, pHead->x);
	cout << "@";


}

//删除节点
void Snake::delPoint()
{
	
	//两个节点以上 才去做删除操作
	if (pHead == NULL || pHead->next == NULL)
	{
		return;
	}

	Point * pCur = pHead->next;
	Point * pPre = pHead;

	while (pCur->next !=NULL)
	{
		pPre = pPre->next;
		pCur = pCur->next;
	}
	//删除尾节点

	wall.setWall(pCur->x, pCur->y, ' ');
	gotoxy1(hOut1, pCur->y * 2, pCur->x);
	cout << " ";

	delete pCur;
	pCur = NULL;
	pPre->next = NULL;

}

bool Snake::move(char key)
{
	int x = pHead->x;
	int y = pHead->y; 

	switch (key)
	{
	case UP:
		x--;
		break;
	case DOWN :
		x++;
		break;
	case LEFT:
		y--;
		break;
	case RIGHT:
		y++;
		break;
	default:
		break;
	}

	//判断 如果是下一步碰到的是尾巴,不应该死亡
	Point * pCur = pHead->next;
	Point * pPre = pHead;

	while (pCur->next != NULL)
	{
		pPre = pPre->next;
		pCur = pCur->next;
	}
	if (pCur->x == x && pCur->y == y)
	{
		//碰到尾巴 循环
		isRool = true;
	}
	else
	{
		//判断用户到达位置是否成功
		if (wall.getWall(x, y) == '*' || wall.getWall(x, y) == '=')
		{
			addPoint(x, y);
			delPoint();
			system("cls");
			wall.drawWall();
			cout << "得分:" << getScore() << "分" << endl;
			cout << "GAME OVER!!!" << endl;
			return false;
		}
	}



	//移动成功 分两种  
	//吃到食物  未吃到食物
	if (wall.getWall(x,y) == '#')
	{
		addPoint(x, y);

		//重新设置食物
		food.setFood();
	}
	else
	{
		addPoint(x, y);
		delPoint();
		if (isRool == true)
		{
			wall.setWall(x, y, '@');
			gotoxy1(hOut1, 2 * y, x);
			cout << "@";
		}
		
	}

	return true;
}

int Snake::getSleepTime()
{
	int sleepTime = 0;

	int size = countList();
	if ( size < 5)
	{
		sleepTime = 300;
	}
	else if (size >=5 && size <=8)
	{
		sleepTime = 200;
	}
	else
	{
		sleepTime = 100;
	}
	return sleepTime;
}

int Snake::countList()
{
	int size = 0;
	Point * curPoint = pHead;
	while (curPoint != NULL)
	{
		size++;

		curPoint = curPoint->next;
	}
	return size;
}

int Snake::getScore()
{
	int size = countList();

	int score = (size -3) * 100;

	return score;
}


食物

#pragma  once
#include <iostream>
#include "wall.h"
using namespace std;

class Food
{
public:
	Food(Wall & tempWall);

	//设置食物
	void setFood();


	int foodX;
	int foodY;

	Wall & wall;
};

食物cpp文件

#include "food.h"
#include <windows.h>
void gotoxy2(HANDLE hOut2, int x, int y)
{
	COORD pos;
	pos.X = x;             //横坐标
	pos.Y = y;            //纵坐标
	SetConsoleCursorPosition(hOut2, pos);
}
HANDLE hOut2 = GetStdHandle(STD_OUTPUT_HANDLE);//定义显示器句柄变量


Food::Food(Wall & tempWall) : wall(tempWall)
{

}

void Food::setFood()
{
	while (true)
	{
		foodX = rand() % (Wall::ROW - 2) + 1;
		foodY = rand() % (Wall::COL - 2) + 1;

		//如果随机的位置是蛇头或蛇身 就重新生成随机数
		if (wall.getWall(foodX, foodY) == ' ')
		{
			
			wall.setWall(foodX, foodY, '#');
			gotoxy2(hOut2, foodY * 2, foodX);
			cout << "#";
			break;
		}
	}
}

程序主文件

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include "wall.h"
#include "snake.h"
#include "food.h"
#include <ctime>
#include <conio.h>
#include <Windows.h>

void gotoxy(HANDLE hOut, int x, int y)
{
	COORD pos;
	pos.X = x;             //横坐标
	pos.Y = y;            //纵坐标
	SetConsoleCursorPosition(hOut, pos);
}
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);//定义显示器句柄变量


int main(){

	//添加随机种子
	srand((unsigned int)time(NULL));

	//是否死亡标示
	bool isDead = false;

	char preKey = NULL;

	Wall wall;
	wall.initWall();
	wall.drawWall();
	
	Food food(wall);
	food.setFood();

	Snake snake(wall, food);
	snake.initSnake();

	//snake.move('w');
	//snake.move('w');
	//snake.move('a');

	
	gotoxy(hOut, 0, Wall::ROW);
	cout << "得分:" << snake.getScore() << "分" << endl;

	//gotoxy(hOut,10, 5); //y*2  x

	//接受用户输入


	while (!isDead)
	{
		char key = _getch();

		//判断 如果是第一次按了 左键 才不能激活游戏
		// 判断 上一次 移动方向
		if (preKey == NULL && key == snake.LEFT)
		{
			continue;
		}

		do 
		{
			if (key == snake.UP || key  == snake.DOWN || key == snake.LEFT || key == snake.RIGHT)
			{
				//判断本次按键 是否与上次冲突
				if ( (key == snake.LEFT && preKey == snake.RIGHT)  ||
					(key == snake.RIGHT && preKey == snake.LEFT) ||
					(key == snake.UP && preKey == snake.DOWN) ||
					(key == snake.DOWN && preKey == snake.UP))
				{
					key = preKey;
				}
				else
				{
					preKey = key; //不是冲突按键  可以更新按键
				}


				if (snake.move(key) == true)
				{
					//移动成功 代码
					//system("cls");
					//wall.drawWall();
					gotoxy(hOut,0, Wall::ROW);
				
					cout << "得分:" << snake.getScore() << "分" << endl;
					Sleep(snake.getSleepTime());
				}
				else
				{
					isDead = true;
					break;
				}
			}
			else
			{
				key = preKey; //强制将错误按键变为 上一次移动的方向
			}
			

		} while (!_kbhit()); //当没有键盘输入的时候 返回0

	
	}

	

	


	测试
	//wall.setWall(5, 4, '=');
	//wall.setWall(5, 5, '=');
	//wall.setWall(5, 6, '@');

	//

	//cout << wall.getWall(0, 0) << endl;
	//cout << wall.getWall(5, 4) << endl;
	//cout << wall.getWall(5, 6) << endl;
	//cout << wall.getWall(1, 1) << endl;

	system("pause");
	return EXIT_SUCCESS;
}

优化

修改光标的代码

通过修改光标的位置,实现局部刷新,减少闪屏

void gotoxy(HANDLE hOut, int x, int y)
{
	COORD pos;
	pos.X = x;             //横坐标
	pos.Y = y;            //纵坐标
	SetConsoleCursorPosition(hOut, pos);
}
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);//定义显示器句柄变量

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
#include<iostream.h> #include<windows.h> #include<time.h> #include<stdlib.h> #include<conio.h> #define N 21 void gotoxy(int x,int y)//位置函数 {COORD pos; pos.X=2*x; pos.Y=y; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),pos); } void color(int a)//颜色函数 { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),a); } void init(int apple[2])//初始化函数(初始化围墙、显示信息、苹果) { int i,j;//初始化围墙 int wall[N+2][N+2]={{0}};for(i=1;i<=N;i++) { for(j=1;j<=N;j++) wall[i][j]=1; } color(11);for(i=0;i<N+2;i++) { for(j=0;j<N+2;j++) { if(wall[i][j]) cout<<"■";else cout<<"□" ; } cout<<endl;}gotoxy(N+3,1);//显示信息 color(20); cout<<"按 W S A D 移动方向"<<endl; gotoxy(N+3,2);color(20); cout<<"按任意键暂停"<<endl; gotoxy(N+3,3);color(20); cout<<"得分:"<<endl;apple[0]=rand()%N+1;//苹果 apple[1]=rand()%N+1;gotoxy(apple[0],apple[1]);color(12); cout<<"●"<<endl; } int main() { int i,j; int** snake=NULL; int apple[2]; int score=0; int tail[2]; int len=3; char ch='p'; srand((unsigned)time(NULL)); init(apple); snake=(int**)realloc(snake,sizeof(int*)*len); for(i=0;i<len;i++)snake[i]=(int*)malloc(sizeof(int)*2); for(i=0;i<len;i++) { snake[i][0]=N/2;snake[i][1]=N/2+i; gotoxy(snake[i][0],snake[i][1]); color(14);cout<<"★"<<endl; }while(1)//进入消息循环 { tail[0]=snake[len-1][0]; tail[1]=snake[len-1][1]; gotoxy(tail[0],tail[1]); color(11);cout<<"■"<<endl; for(i=len-1;i>0;i--) { snake[i][0]=snake[i-1][0]; snake[i][1]=snake[i-1][1]; gotoxy(snake[i][0],snake[i][1]); color(14); cout<<"★"<<endl; } if(kbhit()) { gotoxy(0,N+2); ch=getche(); } switch(ch) { case 'w':snake[0][1]--;break; case 's':snake[0][1]++;break; case 'a':snake[0][0]--;break; case 'd':snake[0][0]++;break; default: break; } gotoxy(snake[0][0],snake[0][1]); color(14); cout<<"★"<<endl; Sleep(abs(200-0.5*score)); if(snake[0][0]==apple[0]&&snake[0][1]==apple[1])//吃掉苹果后蛇分数加1,蛇长加1 { score++;len++; snake=(int**)realloc(snake,sizeof(int*)*len); snake[len-1]=(int*)malloc(sizeof(int)*2); apple[0]=rand()%N+1; apple[1]=rand()%N+1; gotoxy(apple[0],apple[1]); color(12); cout<<"●"<<endl; gotoxy(N+5,3); color(20); cout<<score<<endl; } if(snake[0][1]==0||snake[0][1]==N||snake[0][0]==0||snake[0][0]==N)//撞到围墙后失败 { gotoxy(N/2,N/2); color(30); cout<<"失败!!!"<<endl; for(i=0;i<len;i++)free(snake[i]); Sleep(INFINITE); exit(0); } } return 0; }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值