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");
}
好了,今天小羊的分享到此为止,记得点赞收藏,关注小羊哦。