C语言贪吃蛇
今天把以前自己写的贪吃蛇总结了一下,发到博客上,怕放在电脑上哪天丢失了都不知道, 有不当之处还望指教 (*・ω< ) ヾ(◍°∇°◍)ノ゙
贪吃蛇中, 我们看到的蛇在不断的移动,其实就是在屏幕上不断的打印显示信息,又不断的擦除,就让蛇看起来在移动,在这里要用到语句: system("cls")
, 但这个就造成了一个问题,打印不能一下打印完,所以造成了 闪屏, 解决闪屏的办法是 双缓冲, 双缓冲我在后边介绍
主要思想
大致准备
- 首先是用一个二维数组
arr
来存放屏幕上的布局, 用0来表示空白的地方,蛇可以移动的区域, - 其他的食物、还有最外边的墙都用小于零的数来表示
- 我们的蛇身的话就用大于零的数表示,蛇尾用1表示,从蛇尾到蛇头依次增大,就可以根据蛇头值的大小来判断这个蛇吃了多少的食物
- 还有一些就是键盘上的方向键的, 总行、总列数,我们可以用宏定义, 这样方向键在用的时候就不用写 ASCII码了,直接用标识符又比较明显知道是哪个方向,不易错;
- 还要用一个1行2列的数组来存放蛇头的坐标位置,默认蛇是有长度的,所以要默认在数组中给蛇安排好它的位置,就是把数组中蛇身坐标的位置的值改为从蛇尾0到蛇头依次增大的值,
随机产生食物
- 那我们现在开始随机产生食物,用一个函数
produceFood
专用来随机产生食物 - 随机产生一个坐标垂直
vertical
的水平的level
v = rand() % ROW; l = rand() % COL
判断arr[v][l]
是否是空白区域,如果不是再重新产生,直到产生的是空白区域
方向选择
- 根据按键,判断出是哪个方向,就把相应的方向存好,注意: 如果按的是和蛇移动的方向相反,我们应该不处理,让蛇继续往这个方向移动,不然就会出现蛇吃自己,那就应该死亡了,这样体验要好一些些, 因为有好多时候就这样死亡了体验不好
蛇移动
- 那我们怎么来让蛇移动呢,首先我们要默认给蛇一个移动的方向,还要用一个1行2列的数组来存放蛇头的坐标位置,知道了蛇头的移动方向,还有坐标位置,那我们就知道了蛇头要移动到下一个坐标点的位置,那我们根据我们的数组就知道下一个坐标点位置是什么东西(食物,墙,空白地方,蛇身),我们就可以判断了
- 如果是 空白地方: 首先,我们让整个数组中大于零的值都 减1, 那原先的蛇尾的值为 1 变为了 0 , 就变为空白区域了,蛇尾就移动了, 之后我们让下一个坐标点位置的值改为蛇头的值 加1 ,蛇头就自然移动到下一个位置了
- 如果是 食物:把下一个坐标点位置的值改为蛇头的值 加1,这样蛇移动了,且变长了,吃了一个食物,之后调用 产生食物的函数,继续运行
- 如果是 墙或蛇身:死亡了, 根据蛇头的值,判断出吃了多少食物,得了多少分, 显示分数,退出游戏
双缓冲
写这个贪吃蛇已经快有一年了, 我也忘了双缓冲了(>ω・* )ノφ(>ω<*) ,只能模糊的记得些,让我说我也不知道怎样说, 我太菜了┗( ▔, ▔ )┛,但我代码里面有双缓冲,具体的双缓冲可以看
http://m.bubuko.com/infodetail-582228.html
https://blog.csdn.net/weixinhum/article/details/72179593
可以去网上了解了解 , 好了, 看看代码吧,不足之处欢迎指教 ─=≡Σ(((つ•̀ω•́)つ
代码如下:
/*********************************************
*贪吃蛇
*用0表示空白的地方,用大于0的数做蛇,从蛇尾到蛇头的数依次增大,其它(墙,食物)的用负数表示,
*碰到食物就把食物那个点的值变成蛇头值+1,食物的点变成蛇头,退出
*没有碰到食物,下一个点为空,就把下一个点的值变成蛇头值+1,
*在将整个数组大于0的值都减一,蛇尾为一的自然变为0,就消失了,其它情况直接退出
*@author zhoufei
*@date 2018-06-03
*********************************************/
#include<stdio.h>
#include<windows.h>
#define FOOD -4 //食物
#define WALL -9 //墙
#define COL 35
#define ROW 20
#define VK_UP 72 //方向键上下左右的第二个值,方向键有两个值,第一个是224,分别是72,...
#define VK_DOWN 80
#define VK_LEFT 75
#define VK_RIGHT 77
#define int short //把所有的int类型数据换成short类型节约空间
/*心得,常量大写,变量有多个单词加下划线,函数多个单词首字母小写,其余首字母大写*/
//用word打特殊符号,再复制
void fangXiangChoice(int arr[][COL]); //输入的方向的
void start(int p[][COL]); //开始之前的初始化操作
void print(int* a); //输出函数,输出到屏幕
void produceFood(int a[][COL]); //产生食物
void moveSnake(int (*arr)[COL],int * fx); //蛇的移动
int head_v = 4,head_h = 5;
//蛇头的纵向坐标和横向坐标,用大于0的数做蛇,从蛇尾到蛇头的数依次增大,
int arr[ROW][COL];
int speed_snake = 5; //蛇移动的速度,几次循环执行一次来控制
int fang_xiang = VK_RIGHT;//蛇移动的方向,默认向右
int exitgame = 0;
HANDLE hOutput,hOutBuf; //控制台屏幕缓冲区句柄
HANDLE *houtpoint;
COORD coord = {1,0};
DWORD bytes = 0;
char data[ROW][COL];
int zhixiang_hOutput = 0; //通过指针轮流指向两个缓冲区,实现双缓冲
void main()
{
int k;
start(arr);
produceFood(arr);
puts("嗨~欢迎你来到贪吃蛇的世界!请君坐好了,要开车了~");
Sleep(2000); //休眠2秒
system("cls");
hOutBuf = CreateConsoleScreenBuffer(
GENERIC_WRITE, //定义进程可以往缓冲区写数据
FILE_SHARE_WRITE, //定义缓冲区可共享写权限
NULL,
CONSOLE_TEXTMODE_BUFFER,
NULL
);
hOutput = CreateConsoleScreenBuffer(
GENERIC_WRITE, //定义进程可以往缓冲区写数据
FILE_SHARE_WRITE, //定义缓冲区可共享写权限
NULL,
CONSOLE_TEXTMODE_BUFFER,
NULL
);
SetConsoleTitle(TEXT("贪吃蛇"));
SMALL_RECT rc = {0,0,COL+10,ROW+4}; //设置窗口大小,宽度和高度
SetConsoleWindowInfo(hOutput,1,&rc);
SetConsoleWindowInfo(hOutBuf,1,&rc);
//隐藏两个缓冲区的光标
CONSOLE_CURSOR_INFO cci;
cci.bVisible = 0;
cci.dwSize =1;
SetConsoleCursorInfo(hOutput, &cci);
SetConsoleCursorInfo(hOutBuf, &cci);
while(1)
{
if(kbhit())
{
fangXiangChoice(arr);
moveSnake(arr,&fang_xiang);
print(arr[0]);
}
if(k++ % speed_snake == 0)
{
moveSnake(arr,&fang_xiang);
print(arr[0]);
}Sleep(40);
}
}
void fangXiangChoice(int arr[][COL])
{
int key1,key2;
key1 = getch();
if(key1 == 27)//ESC建退出
exitgame = 1;
if(key1 == 224)
{
key2 = getch();
switch(key2)
{
case VK_UP: if(fang_xiang != VK_DOWN) fang_xiang = VK_UP; //如果在向下移动,那蛇就不能向上移动,下面的相同
break;
case VK_DOWN: if(fang_xiang != VK_UP) fang_xiang = VK_DOWN;
break;
case VK_LEFT: if(fang_xiang != VK_RIGHT) fang_xiang = VK_LEFT;
break;
case VK_RIGHT: if(fang_xiang != VK_LEFT) fang_xiang = VK_RIGHT;
}
}
}
void start(int p[][COL])
{
int i,j;
p[head_v][head_h] = 2; //初始化蛇头,蛇身
p[head_v][head_h-1] = 1;
for(i = 0; i < ROW; i++)
{
for(j = 0; j < COL; j++)
{
if(i == 0 || i == ROW-1 || j == 0 || j == COL-1)
p[i][j] = WALL; //初始化墙
}
}
}
void produceFood(int a[][COL])
{
int food_v,food_h;
srand(time(NULL));
food_v = rand() % ROW;
food_h = rand() % COL;
while(a[food_v][food_h] != 0)
{
food_v = rand() % ROW;
food_h = rand() % COL;
}
a[food_v][food_h] = FOOD;
}
/**
//蛇头的纵向坐标和横向坐标,用大于0的数做蛇,从蛇尾到蛇头的数依次增大,
//碰到食物就把食物那个点的值变成蛇头值+1,食物的点变成蛇头,退出
//没有碰到食物,下一个点为空,就把下一个点的值变成蛇头值+1,
//在将整个数组大于0的值都减一,蛇尾为一的自然变为0,就消失了,其它情况直接退出
*/
void moveSnake(int (*arr)[COL],int * fx) //根据传入的方向移动蛇
{
int i,j,eat_food = 0; //为0表示没有吃到食物
int temp = arr[head_v][head_h]; //记录现在蛇头的值
if(*fx == VK_UP)
{
if(arr[head_v - 1][head_h] == 0) head_v--;
else if(arr[head_v - 1][head_h] == FOOD)
head_v--,eat_food = 1; //吃到食物了,蛇身不用减一
else exitgame = 1; //其它情况只有撞墙和吃蛇身
}if(*fx == VK_DOWN)
{
if(arr[head_v + 1][head_h] == 0) head_v++;
else if(arr[head_v + 1][head_h] == FOOD)
head_v++,eat_food =1;
else exitgame = 1;
}if(*fx == VK_LEFT)
{
if(arr[head_v][head_h -1] == 0) head_h--;
else if(arr[head_v][head_h -1] == FOOD)
head_h--,eat_food = 1;
else exitgame = 1;
}if(*fx == VK_RIGHT)
{
if(arr[head_v][head_h + 1] == 0) head_h++;
else if(arr[head_v][head_h + 1] == FOOD)
head_h++,eat_food = 1;
else exitgame = 1;
}
/**没有吃的食物大于零的数都要减一*/
arr[head_v][head_h] = temp+1;
if(!eat_food)
{
for(i = 0; i < ROW; i++)
{
for(j = 0; j < COL; j++)
{
if(arr[i][j] > 0) arr[i][j] = arr[i][j] - 1;
}
}
}
else
produceFood(arr); //吃到食物了,产生一个食物
}
void print(int* p)
{
int i,j;
char shuo_ming1[] = "请按方向键 ↑ ↓ ← → 的控制上下左右";
char shuo_ming2[] = "连续按方向键会加速,退出按ESC键";
for(i = 0; i < ROW; i++)
{
for(j = 0; j < COL; j++,p++)
{
if(*p == WALL)
data[i][j] = '*'; //printf("■"); //墙墙
if(*p == 0)
data[i][j] = ' '; //printf(" ");
if(*p > 0) //蛇
{
if(i == head_v && j == head_h) data[i][j] = '0';//printf("⊙");
else data[i][j] = '@'; //printf("●");
}
if(*p == FOOD)
data[i][j] = '@'; //printf("●");
}//printf("\n");
}
//以下是缓冲代码
zhixiang_hOutput = !zhixiang_hOutput ;
if(!zhixiang_hOutput)
{
houtpoint = &hOutput;
}
else
{
houtpoint = &hOutBuf;
}
coord.Y = 1;
WriteConsoleOutputCharacterA(*houtpoint,shuo_ming1,strlen(shuo_ming1), coord,&bytes);
coord.Y++; WriteConsoleOutputCharacterA(*houtpoint,shuo_ming2,strlen(shuo_ming2), coord,&bytes);
for(i = 0; i < ROW; i++)
{
coord.Y++ ;
WriteConsoleOutputCharacterA(*houtpoint,(char *)data[i],COL, coord,&bytes);
}
if(exitgame)
{
char score[20];//得分为蛇吃的食物乘以3
sprintf(score,"你的得分为:%d",(arr[head_v][head_h]-2)*3);
coord.Y++;
WriteConsoleOutputCharacterA(*houtpoint,score,strlen(score), coord,&bytes);
SetConsoleActiveScreenBuffer(*houtpoint);
getch();exit(0);
}
SetConsoleActiveScreenBuffer(*houtpoint);
}