贪吃蛇小游戏------C语言

本文介绍了贪吃蛇游戏的开发,通过视图、模型、控制器的模块化设计,详细讲解了蛇、食物、随机生成和界面处理等关键模块的实现,包括蛇的单链表实现、食物的生成与消失、地图的绘制和更新。同时,提到了多线程在游戏中的应用,以及如何处理用户输入和游戏规则,如蛇的移动、碰撞检测和得分系统。

Hallo,各位小伙伴们,大家好!

今天,我想和大家介绍一款小游戏,就是贪吃蛇游戏,相信大家都玩过贪吃蛇小游戏吧,话不多说,让我们一起进入正题吧!

首先,我们先来做个大概的项目分析,我们可以把贪吃蛇这个游戏分为三个模块:视图、模型、控制器 

看个思维导图吧。

 模块设计

1.蛇模块:蛇吃了食物后会增长,不是固定长度,选用链表更为合适,这里选用单链表来模拟蛇。蛇这一模块主要完成关于蛇的一系列动作。

2.食物模块:食物模块主要是控制食物随机出现的种类和位置,食物在出现时,不能够出现在蛇的身体上,障碍物上;当蛇把食物吃掉后,食物要消失。

3.随机生成模块:由于项目中需要用到一些随机数,比如随机选择食物的种类,随机摆放食物的位置等,所以设计了随机生成模块。

4.界面处理模块:游戏运行的整个界面称之为地图,而界面处理模块主要就是实现地图的加载、预处理地图、和地图的显示的等功能,直观形象地显示蛇、食物、障碍物以及实时得分等界面信息,必须先要有地图才可以有蛇及食物、障碍物的哦。

多学一招:多线程

那再来个思维导图来帮助大家理解

 那我们一起来个个模块进行吧

 模块分析

地图模块

void initWall()//打印墙
{
	for (int i = 0; i < ROW; i++)//1.要用两个for是因为长和宽里面的范围都需要打印,2.打印行
	{
		for (int j = 0; j < COL; j++)//打印列
		{
			if (i==0 || j==0 || i==ROW-1 || j==COL-1)//如果行和列是0或是行列最大值减掉1,这个时候就用*打印围墙
			{

				gameArray[i][j] = '*';

			}
			else//其他地方用空字符来表示
			{
				gameArray[i][j] = ' ';
			}

		}

	}


}

//打印墙
void drawWall()
{
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			printf("%c ", gameArray[i][j]);
		}
		if (i == 5)
		{
			printf("贪吃蛇游戏");
		}
		if (i == 6)
		{
			printf("up:w");
		}
		if (i == 7)
		{
			printf("right:d");
		}
		if (i == 8)
		{
			printf("down:s");
		}
		if (i == 9)
		{
			printf("left:a");
		}


		printf("\n");
	}

}
//设置场景中的字符
void setWall(int x, int y, char key)
{
	gameArray[x][y] = key;
}
//获取场景中的字符
char getWall(int x, int y)
{
	return gameArray[x][y];
}

void gotoXY(HANDLE hout, int x, int y)
{
	COORD pos;//将x,y出现在显示屏上
	pos.X = x;
	pos.Y = y;
	SetConsoleCursorPosition(hout, pos);//移动命令行中光标的位置
}
//移动光标打印字符
void printChar(int x, int y, char key)
{
	HANDLE ss = GetStdHandle(STD_OUTPUT_HANDLE);//获取控制台句柄
	gotoXY(ss, x, y);//将光标移动到指定位置
	printf("%c", key);

}

 蛇模块

struct Point//蛇结构
{
	int x;
	int y;
	struct Point* next;
};

enum EDirection//方向
{
	UP='w',DOWN='s',LEFT='a',RIGHT='d'
};
//初始化蛇
void initSnake();

//销毁蛇
void destroyPoint();

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

//删除结点
void delPoint();

enum EDirection direction;


//蛇头指针
struct Point* pHead;

//蛇的坐标
int x;
int y;




 struct pointer_safety//蛇身的一个结点
 {
     int x;
     int y;
struct Point*next;
 };

 void initSnake()
 {
     pHead = NULL;
     addPoint(5,3);
     addPoint(5,4);
     addPoint(5,5);
     addPoint(5,6);

 }
 void destroyPoint()//
 {
     struct Point* cur =pHead;
     while (pHead !=NULL)//如果蛇头不为空,就减掉蛇头的后面结点直到只剩下蛇头。
     {
         cur =pHead->next;
         free(pHead);
         pHead = cur;
     }
 }
 void addPoint(int x,int y)//
 {
     struct Point* newPoint =(struct Point*)malloc(sizeof(struct Point));//生成新的结点
     if(newPoint == NULL)//新结点不为空就返回
        {
            return;
        }

         newPoint->x= x;
       newPoint->y=y;
       newPoint->next =NULL;

       if(pHead !=NULL)
       {
           setWall(pHead->x,pHead->y,'=');//新的结点添加到链表头部
           printChar(pHead->y*2,pHead->x,'=');
       }
       newPoint->next =pHead;//新的结点变成头结点
       pHead = newPoint;
       setWall(pHead->x,pHead->y,'@');//生成新的@
       printChar(pHead->y*2,pHead->x,'@');
 }
 void delPoint()
{
	//2个结点以上才删除
	if (pHead == NULL || pHead->next == NULL)
	{
		return;
	}

	struct Point* pre = pHead;//用两个临时节点,一个是前一个节点pre,一个是当前节点cur
	struct Point* cur = pHead->next;

	while (cur->next!=NULL)//指向下一个不为空就循环
	{
		pre = pre->next;
		cur = pre->next;

	}
	setWall(cur->x, cur->y, ' ');
	printChar(cur->y * 2, cur->x, ' ');
	free(cur);
	cur = NULL;
	pre->next = NULL;

}

 食物模块

//设置食物
void setFood();
//设置障碍物
void setOb();

int foodX;
int foodY;
//设置食物
void setFood()
{
	srand((unsigned int)time(NULL));//使用当前时间作为种子来初始化随机数生成器
	while (1)
	{
		foodX = rand() % (ROW - 2) + 1;//食物不能够生成在墙上,取余可以让它不出现在墙上,因为前面定义ROW,COL为26,取余后就是0-23,当然不能生成在0那里,所以就要+1,变成1-24
		foodY = rand() % (COL - 2) + 1;

		if (getWall(foodX,foodY)== ' ')
		{
			//只有空格的地方可以设置食物
			setWall(foodX, foodY, '$');
			printChar(foodY * 2, foodX, '$');
			break;
		}
	}

}

//设置障碍物
void setOb()
{
	srand((unsigned int)time(NULL));//使用当前时间作为种子来初始化随机数生成器
	while (1)
	{
		foodX = rand() % (ROW - 2) + 1;
		foodY = rand() % (COL - 2) + 1;

		if (getWall(foodX, foodY) == ' ')
		{
			//只有空格的地方可以设置障碍物
			setWall(foodX, foodY, '*');
			printChar(foodY * 2, foodX, '*');
			break;
		}
	}
}

全部代码如下

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include <time.h>
#include <windows.h>//
#include <process.h> //开启线程的头文件

enum//枚举
{
	ROW=26,COL=26

};

//初始化墙
void initWall();
//打印墙
void drawWall();
//设置场景中的字符
void setWall(int x, int y, char key);
//获取场景中的字符
char getWall(int x, int y);
//定义二维数组
char gameArray[ROW][COL];
//移动光标打印字符
void printChar(int x, int y, char key);
//初始化墙(初始化二维数组)
void initWall()
{
	for (int i = 0; i < ROW; i++)//要用两个for是因为长和宽里面的范围都需要打印
	{
		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 drawWall()
{
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			printf("%c ", gameArray[i][j]);
		}
		if (i == 5)
		{
			printf("贪吃蛇游戏");
		}
		if (i == 6)
		{
			printf("up:w");
		}
		if (i == 7)
		{
			printf("right:d");
		}
		if (i == 8)
		{
			printf("down:s");
		}
		if (i == 9)
		{
			printf("left:a");
		}


		printf("\n");
	}

}
//设置场景中的字符
void setWall(int x, int y, char key)
{
	gameArray[x][y] = key;
}
//获取场景中的字符
char getWall(int x, int y)
{
	return gameArray[x][y];
}

void gotoXY(HANDLE hout, int x, int y)
{
	COORD pos;
	pos.X = x;
	pos.Y = y;
	SetConsoleCursorPosition(hout, pos);//移动命令行中光标的位置
}
//移动光标打印字符
void printChar(int x, int y, char key)
{
	HANDLE ss = GetStdHandle(STD_OUTPUT_HANDLE);//获取控制台句柄
	gotoXY(ss, x, y);//将光标移动到指定位置
	printf("%c", key);

}

struct Point
{
	int x;
	int y;
	struct Point* next;
};

enum EDirection
{
	UP='w',DOWN='s',LEFT='a',RIGHT='d'
};
//初始化蛇
void initSnake();

//销毁蛇
void destroyPoint();

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

//删除结点
void delPoint();

enum EDirection direction;


//蛇头指针
struct Point* pHead;

//蛇的坐标
int x;
int y;




 struct pointer_safety//蛇身的一个结点
 {
     int x;
     int y;
struct Point*next;
 };

 void initSnake()
 {
     pHead = NULL;
     addPoint(5,3);
     addPoint(5,4);
     addPoint(5,5);
     addPoint(5,6);

 }
 void destroyPoint()//
 {
     struct Point* cur =pHead;
     while (pHead !=NULL)
     {
         cur =pHead->next;
         free(pHead);
         pHead = cur;
     }
 }
 void addPoint(int x,int y)//
 {
     struct Point* newPoint =(struct Point*)malloc(sizeof(struct Point));
     if(newPoint == NULL)
        {
            return;
        }

         newPoint->x= x;
       newPoint->y=y;
       newPoint->next =NULL;

       if(pHead !=NULL)
       {
           setWall(pHead->x,pHead->y,'=');
           printChar(pHead->y*2,pHead->x,'=');
       }
       newPoint->next =pHead;
       pHead = newPoint;
       setWall(pHead->x,pHead->y,'@');
       printChar(pHead->y*2,pHead->x,'@');
 }
 void delPoint()
{
	//2个结点以上才删除
	if (pHead == NULL || pHead->next == NULL)
	{
		return;
	}

	struct Point* pre = pHead;
	struct Point* cur = pHead->next;

	while (cur->next!=NULL)
	{
		pre = pre->next;
		cur = pre->next;

	}
	setWall(cur->x, cur->y, ' ');
	printChar(cur->y * 2, cur->x, ' ');
	free(cur);
	cur = NULL;
	pre->next = NULL;

}
//设置食物
void setFood();
//设置障碍物
void setOb();

int foodX;
int foodY;
//设置食物
void setFood()
{
	srand((unsigned int)time(NULL));//使用当前时间作为种子来初始化随机数生成器
	while (1)
	{
		foodX = rand() % (ROW - 2) + 1;
		foodY = rand() % (COL - 2) + 1;

		if (getWall(foodX,foodY)== ' ')
		{
			//只有空格的地方可以设置食物
			setWall(foodX, foodY, '$');
			printChar(foodY * 2, foodX, '$');
			break;
		}
	}

}

//设置障碍物
void setOb()
{
	srand((unsigned int)time(NULL));//使用当前时间作为种子来初始化随机数生成器
	while (1)
	{
		foodX = rand() % (ROW - 2) + 1;
		foodY = rand() % (COL - 2) + 1;

		if (getWall(foodX, foodY) == ' ')
		{
			//只有空格的地方可以设置障碍物
			setWall(foodX, foodY, '*');
			printChar(foodY * 2, foodX, '*');
			break;
		}
	}
}
char ch = NULL;

void MainLop()
{
	char preKey=NULL;//记录上一次蛇运动的方向
	while (1)
	{
		ch = _getch();
		//如果是蛇没有运动
		if (ch==NULL && preKey==NULL)
		{
			continue;
		}
		if ((ch==UP && preKey==DOWN)||
			(ch == DOWN && preKey == UP)||
			(ch == LEFT && preKey == RIGHT) ||
			(ch == RIGHT && preKey == LEFT))
		{
			ch = preKey;
		}
		else
		{
			preKey = ch;
		}

		switch (ch)
		{
		case 'w':
		case 'W':
			direction = UP;
			break;
		case 'a':
		case 'A':
			direction = LEFT;
			break;
		case 's':
		case 'S':
			direction = DOWN;
			break;
		case 'd':
		case 'D':
			direction = RIGHT;
			break;
		default:
			break;
		}
	}

}

//计算分数
int score = 0;
//速度
int st = 500;

//关卡数
int ckp = 0;

//重新设置变量的初始值
void clearGame()
{
	system("cls");
	ch = NULL;
	score = 0;
	st = 500;
	ckp++;
}

void main()
{
	AA:
	//===@
	//===@

	//初始化墙
	initWall();

	//打印墙
	drawWall();

	//初始化蛇
	initSnake();

	//设置食物
	setFood();

	for (int i = 0; i < ckp; i++)
	{
		setOb();
	}

	//开启线程
	HANDLE h;
	h = (HANDLE)_beginthread(MainLop, 0, NULL);
	HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO cursorInfo;
	GetConsoleCursorInfo(consoleHandle, &cursorInfo);
	cursorInfo.bVisible = FALSE;  // 将光标设置为不可见
	SetConsoleCursorInfo(consoleHandle, &cursorInfo);
	while (1)
	{
		//一开始蛇不会移动
		if (ch==NULL)
		{
			continue;
		}

		//计算速度
		if (score >= 300 && score < 500)
		{
			st = 300;
		}
		if (score >= 500 && score < 800)
		{
			st = 100;
		}
		Sleep(300);

		x = pHead->x;
		y = pHead->y;
		switch (direction)
		{
		case UP:
			x--;
			break;
		case DOWN:
			x++;
			break;
		case LEFT:
			y--;
			break;
		case RIGHT:
			y++;
			break;
		default:
			break;
		}
		//判断蛇是否可以往前
		if (getWall(x, y) == '=' || getWall(x, y) == '*')
		{
			break;
		}
		//正常移动
		if(getWall(x,y)=='$')
		{
			addPoint(x, y);
			//设置食物
			setFood();
			//设置障碍物
			setOb();
			score += 100;
			if (score==800)
			{
				//重新设置变量的初始值
				clearGame();
				goto AA;
			}
		}
		else
		{
			addPoint(x, y);
			delPoint();
		}

	}

	printChar(0,ROW,' ');
	printf("得分:%d 分\n", score);
    destroyPoint();
	system("pause");

}

好了,今天小羊的分享到此为止,记得点赞收藏,关注小羊哦。

评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值