#include <iostream>
#include <graphics.h>
#include <stdio.h>
#include <conio.h>
#include <math.h>
using namespace std;
#define BLOCK_SIZE 20//注意,不要等于号=,也不要分号;
#define WIDTH 40
#define HEIGHT 30
//全局变量定义
int Blocks[HEIGHT][WIDTH];//注意二维数组第一格是行号,第二个是列号,所以第一格是HEIGHT而不是WIDTH,这个数组是30行40列的数组
char moveDirection;//方向定义为全局变量
int isFailure;
int makeFood = 1;
static int foodplace_i = 0; static int foodplace_j = 0;//要定义为静态变量
//初始化函数
void startup()//初始化蛇身位置、颜色
{
int i;
Blocks[HEIGHT / 2][WIDTH / 2] = 1;
for (int i = 1; i < 6; i++)//从中间开始画贪吃蛇,设头部的数值为1.存入数组中,从头到尾依次增大,尾巴数值为4
{
Blocks[HEIGHT / 2][WIDTH / 2 - i] = i + 1;
}
initgraph(WIDTH * BLOCK_SIZE, HEIGHT * BLOCK_SIZE);
setlinecolor(RGB(200, 200, 200));//线条颜色设为白色,即格子与格子之间的空隙设为白色,比较好看
BeginBatchDraw();//批量绘制,防止屏幕闪烁
}
//绘制函数
void show()//把蛇身和其他方格都画出来
{
cleardevice();//清屏
for (int i = 0; i < HEIGHT; i++)//一行行遍历
for (int j = 0; j < WIDTH; j++)
{
if (Blocks[i][j] > 0)//遍历数组,数值大于0的就是贪吃蛇身体部分,需要填色
{
setfillcolor(HSVtoRGB(Blocks[i][j] * 10, 0.9, 1));/*用数组中存储的连续的数值(从1到4)来为贪吃蛇
上色就可以得到渐变色,但是1到4之间跨度太小,需要*10,颜色范围为10到40,可得较好看的渐变色*/
}
else
setfillcolor(RGB(150, 150, 150));//数值不大于0则不是贪吃蛇身体,格子设置为灰色
fillrectangle(j * BLOCK_SIZE, i * BLOCK_SIZE, (j + 1) * BLOCK_SIZE, (i + 1) * BLOCK_SIZE);//填充颜色
}
if (isFailure)//如果游戏失败
{
setbkmode(TRANSPARENT);//文字字体透明
settextcolor(RGB(255, 0, 0));//设定文字颜色
settextstyle(80, 0, _T("宋体"));//设定文字大小、样式
outtextxy(240, 220, _T("游戏失败"));//输出文字内容,注意用双引号而不是单引号!
}
while(makeFood == 1)
{
foodplace_i = rand() % (HEIGHT - 1) + 1;
foodplace_j = rand() % (WIDTH - 1) + 1;
if (Blocks[foodplace_i][foodplace_j] == 0)
{
setfillcolor(GREEN);
fillrectangle(foodplace_j * BLOCK_SIZE, foodplace_i * BLOCK_SIZE, (foodplace_j + 1) * BLOCK_SIZE, (foodplace_i + 1) * BLOCK_SIZE);
makeFood = 0;
break;
}
else
continue;
}
setfillcolor(GREEN);
fillrectangle(foodplace_j * BLOCK_SIZE, foodplace_i * BLOCK_SIZE, (foodplace_j + 1) * BLOCK_SIZE, (foodplace_i + 1) * BLOCK_SIZE);
FlushBatchDraw();
}
void moveSnake()//这个函数只需要实现12345这几个数值的整体向右移一格,然后放入主函数的while(1)中循环,
{
for (int i = 0; i < HEIGHT; i++)//把蛇身的五个点的数值+1,从蛇头到尾变成23456
for (int j = 0; j < WIDTH; j++)
if (Blocks[i][j] > 0)
Blocks[i][j]++;
int oldTail_i, oldTail_j, oldHead_i, oldHead_j;//定义变量存储旧的蛇尾蛇头的位置
int max = 0; oldHead_i = 0; oldHead_j = 0;
for (int i = 0; i < HEIGHT; i++)
for (int j = 0; j < WIDTH; j++)
{
if (max < Blocks[i][j])
{
max = Blocks[i][j];
oldTail_i = i;
oldTail_j = j;//遍历找到最大值,最大值就是旧的蛇尾
}
if (Blocks[i][j] == 2)
{
oldHead_i = i;
oldHead_j = j;//遍历找到等于2的值,这就是旧的蛇头的位置
}
}
int newHead_i = oldHead_i;
int newHead_j = oldHead_j;
newHead_j = oldHead_j + 1;//要做好初始化先,如果用户没有输入方向的话就先默认往右移动
if (moveDirection == 'w')//向上的话就是行数-1,列数不变
{
newHead_i = oldHead_i-1;
newHead_j = oldHead_j;
}
else if (moveDirection == 'a')//向左就是蛇头列数-1,行数不变
{
newHead_i = oldHead_i ;
newHead_j = oldHead_j - 1;
}
else if (moveDirection == 's')
{
newHead_i = oldHead_i+1;
newHead_j = oldHead_j ;
}
else if (moveDirection == 'd')
{
newHead_i = oldHead_i ;
newHead_j = oldHead_j+1;
}
if (newHead_i < 0 || newHead_i >= HEIGHT || newHead_j < 0 || newHead_j >= WIDTH || Blocks[newHead_i][newHead_j]>0)
{//前四种条件是贪吃蛇碰到边界,最后一个是贪吃蛇头碰到自身
isFailure = 1;
return;
}
if (newHead_i == foodplace_i && newHead_j == foodplace_j)//蛇头碰到食物时
{
Blocks[newHead_i][newHead_j] = 1;//找到之后就把就蛇头的右边那个格子的数值设为1,变为新的蛇头
Blocks[oldTail_i][oldTail_j] = 6;//旧蛇尾的数值设为6,贪吃蛇就会从尾巴长一格
makeFood = 1;//吃了之后就要重新生成食物
}
else//没有碰到食物,只是单纯移动
{
Blocks[newHead_i][newHead_j] = 1;//找到之后就把就蛇头的右边那个格子的数值设为1,变为新的蛇头
Blocks[oldTail_i][oldTail_j] = 0;//旧蛇尾的数值设为0
}
}
//与输入无关的更新
void updateWithouInput()
{
static int waitIndex = 1;//静态局部变量,初始化时为1
waitIndex++;//每一帧+1
if (waitIndex == 20)//如果等于10才执行,这样小蛇每隔10帧移动一次
{
moveSnake();//调用小蛇移动函数
waitIndex = 1;//再变成1
}
}
//与输入有关的更新
void updateWithInput()
{
if (_kbhit()&&isFailure==0)//如果有输入并且游戏没有失败
{
char input=_getch();
if (input == 'w' || input == 'a' || input == 's' || input == 'd')
{
moveDirection = input;
moveSnake();
}
}
}
int _tmain(int argc, _TCHAR* argv[])//主函数
{
startup();
while (1)
{
show();
updateWithouInput();
updateWithInput();
}
_getch();
closegraph();
return 0;
}
/*
要实现按键操控小蛇移动:
问题1 怎么改变方向?
贪吃蛇第1部分时让小蛇往右移动是同样的道理。关键只在于蛇头newHead_i,newHead_j怎么变化
问题2 在updateWithInput函数里判断得出的moveDirection怎么传参给moveSnake函数?
直接把moveDirection定义为全局变量,判断完后直接调用moveSnake
问题3 Sleep函数在让画面停顿的同时也会让updateWithInput之类的函数停顿,小蛇的转向可能会不快速,怎么改进?
把updateWithouInput()里的sleep函数丢掉,使得每10帧小蛇才移动一次,如果改成30帧小蛇会移动得更慢,但是没有了sleep的影响,转向还是会很迅速
问题4 为什么updateWithouInput()和updateWithInput()函数都有调用moveSnake?
因为updateWithouInput()就是没有用户输入时也进行的更新,初始化moveDirection向右后,就算用户没有输入,小蛇也会一直向右移动,while(1)会使
函数中的moveSnake函数一直触发。而updateWithInput()会在用户输入后改变小蛇移动方向,所以也要调用moveSnake函数
bug1
碰到边界后还会移动
bug2
吃了食物后尾巴会呆住一会儿才能跟上蛇
*/