【C++】贪吃蛇小游戏--有图形界面和背景音乐

 贪吃蛇是一款经典的小游戏,曾经在一些老式的手机上都是有的,今天我们就来实现一下这个小游戏。
 首先是写代码钱的前期准备工作,我们这次实现的是带图形界面的贪吃蛇,必须要先下一个图形界面库,方便实现图形界面,这个图形界面库的名字叫做Easyx,可以在网上下载
这里写图片描述
直接进入下载就可以,然后装在vs的安装目录下:
 首先我们要玩贪吃蛇需要两个部分蛇头和蛇身,我们先要定义两个宏一个控制蛇的最大节数,一个控制蛇的的尺寸,方便调整,我们设置蛇的长度为1000节,蛇的尺寸是20像素点,然后要定义蛇的结构体,我们知道蛇的属性很多,包括蛇的重量,蛇的长短,蛇的种类,蛇有没有毒等等很多这里当然我们用不到这么多只需要几个简单的属性,能帮助我们实现相关的功能就可以了,我们要的蛇的属性包括,蛇的节数、蛇的座标,蛇运动的方向,这里的座标我们也用一个结构体来表示,蛇的相关信息表示玩,但是贪吃蛇还有一个重要的部分,是什么呢?对,就是食物,所以我们还要定义一个食物的结构体,食物当然也有食物的属性,包括食物的座标以及一个标志位,这个标志位用来表示是否需要产生食物,相关信息都表示完了,我们就可以正式写我们的游戏了。
 先写我们的初始化函数,我们先写游戏的窗口,利用图形库中的函数就可以了,我们将窗口的高度设置为480,宽度设置成640,单位是像素点,完成之后如图所示:
这里写图片描述

 确实是什么都没有就是一个窗口,将来我们设计的蛇就要在这个里面跑了。
 接下来我们要对蛇的属性先进行初始化,将蛇的节数设置为1,蛇的座标设置成0,0,先让蛇在左上角出现,注意我们这里的座标和数学上的座标是不一样的,左上角座标是0,然后向下和向右递增,数学上是向下是递减,是不一样的。然后将蛇的初始方向设置成右,也可以设置成下,但是左和上是不可以的,要不蛇直接就撞墙了。然后对食物属性进行初始化,这里食物应该是随机产生,所以不能初始化,不然食物的位置被固定就没办法随机了,我们先将食物的标志位设置为0,表示需要产生食物,具体怎么用在下面介绍,到这里初始化函数就完成了,这样完成后还是没有什么效果的。因为我们还没有将蛇绘制出来。
 下面我们来绘制蛇,绘制蛇分为两个部分一部分是绘制蛇头,一部分是绘制蛇身。我们先来写绘制蛇头,这里使用图形库中的一个函数,fillrectangle(),这个函数可以绘制一个矩形出来,但是我们直到要绘制一个矩形就必须要知道它的对角的两个座标,所以这个函数的四个参数就是两个顶点的座标,这样就可以绘制出来蛇头了
这里写图片描述
 这就是我们绘制出来的蛇头,现在还是比较简陋的只有一个小矩形。下面我们来绘制蛇身,蛇身和蛇头一样使用fillrectangle()函数来绘制,这里我们直到蛇身是不断增长的,所有需要用一个循环来绘制,蛇头我们已经绘制了,所以蛇身就需要绘制蛇的节数减一个,这里我们从后向前绘制,运行之后我们发现还是和上图一样的结果,这是因为,蛇的节数是1,也就是只有蛇身的情况,别着急我们继续写。
 我们直到蛇要会移动的,而且蛇的移动是自己动的,不是人操作,人只能控制蛇的转向,下面我们就来写一下,蛇移动的函数,我们知道蛇的转向是很灵活,我们观察发现蛇头后面的蛇身的位置就是上一次蛇头的位置,后面跟着的其他蛇身也是前面蛇身前一次的位置,所以蛇的移动就是把前一次的座标赋给这次的结点,考虑清楚这些后就知道蛇的移动是怎么移动的了,蛇往哪个方向移动呢,这里用一个switch语句来判断,判断的内容就是蛇的方向,蛇的方向向右的化,座标每次要加上蛇的尺寸,然后移动,其他方向也是一样,因为蛇是一个矩形不是一个点座标每次移动的大小就必须是蛇的尺寸,才能显示蛇走了一步。将蛇的移动函数进行测试,可以得到下图:
这里写图片描述
&cmsp;我们发现并不是我们想要的结果,直接就是一条直线,这是因为蛇走的太快了,我们加上Sleep()函数,让每次绘制蛇之后等待300毫秒,
这里写图片描述
 我们发现还是不对,蛇的轨迹连在一起不是我们想要的结果,这是因为我们没有刷新屏幕,所以就会在原来的基础上进行移动,我们调用一下cleardevice()函数刷新一下屏幕,再试一下:
这里写图片描述
 现在就是我们想要的结果了,可是我们发现这是一条失控的蛇,不论按那个按键,蛇都按着自己的想法直接冲到右边,不会拐弯,这是因为,我们没有读入键盘信息,现在我们来写一下控制蛇的函数,还是一个switch语句,判断的就是键盘的输入,利用getch()函数来判断(vs中getch()会有警告我们可以选择忽略或者使用_getch()),利用判断键盘的键码值来判断,是哪个方向。
这里写图片描述
 我们又发现只有当我们输入一次方向键蛇才会走一步,这是为什么呢?这是因为程序一直在等待键盘输入,如果键盘不输入,它就一直等待输入,不会进行下一步,这里我们使用kbhit()函数来解决,用!kbhit()表示无键盘输入情况,定义一个while循环将要执行的程序除了控制蛇的移动以外都放进这个循环中,这样只有当有键盘输入的时候它才会跳出循环,判断要走的方向是哪一个,进而控制蛇的移动。
这里写图片描述
 这次我们发现就可以正常的走了,当输入一个方向时才会改变反向,然后按照新输入的方向继续往下走,蛇能正常走了下来我们就要考虑食物的问题了,我们先来生成食物的座标,还记得我们设置食物结构体的时候设置的标志位吗,这时候就要用到了,我们知道食物应该是随机产生的,所以我们用随机函数来产生了,为了使产生的座标合法我们还需要考虑,让产生的座标在这个界面内,我们直到界面的宽度是640,高度是480,我们的食物也是一个矩形所以必须考虑矩形的大小,所以横座标的范围就是0-620,y座标的范围是0-460,又知道蛇每次移动的距离是20,所以横座标是0-31,纵座标是0-23,所以横座标%32,纵座标%24两个分别乘以SIZE(蛇的尺寸);然后将食物的flag设为1,表示不再需要产生食物。
 下面我们就要绘制食物的图形,这里调用roundrect()函数,这个函数可以绘制一个圆角矩形,这个函数的参数除了矩形的对角两个顶点以外还要直到圆角的弧度,这里给成10,我们还可以设置线条的颜色,这里我们把线条颜色设置成蓝色,运行之后得到:
这里写图片描述
我们可以看到左上角生成的食物了,但是我们发现,当蛇走到食物的位置的时候蛇没有变化食物也没有变化,这是我们还没有写吃食物的函数,下面写一下吃食物的函数,吃食物也就是蛇头的座标和食物的座标相同,当两个座标相同的时候,我们将蛇的节数增加,然后将食物的flag置为0,下次再次进行判断,如果食物的flag如果是0的话重新生成食物座标,就会自动重新生成食物了,下面我们再测试一下:
这里写图片描述
  这次我们发现当蛇吃掉食物之后,蛇的长度就会总动增长,食物也会更新,可以实现基本的功能了,可是我们的游戏还没有设置游戏结束的条件,我们知道贪吃蛇游戏的结束条件是蛇碰到墙或者蛇头碰到蛇身,我们来设计一下游戏结束的条件,蛇头碰到墙也就是蛇的座标超过了界面,也就是x不在0-620之间,y不在0-460之间,游戏结束,然后每次移动用循环判断蛇头的座标和蛇身的座标是不是相同,如果相同就游戏结束,游戏结束的现象就是在屏幕上输出一个Game Over,我们使用outtextxy()函数来设置在界面上展示的文字以及大小,还可以设置字体格式,字体颜色。我们游戏结束时可以先刷新一下屏幕,游戏结束只显示游戏结束那几个字就可以了,为了我们能看见游戏结束几个字,我们可以调用Sleep函数等待3s时间,然后退出程序。
 然后我们最后再来验证一下:
这里写图片描述
游戏就可以结束了。
下来就是加入图像界面,我们首先把自己找到的图片放在目录下面,
这里写图片描述
首先定义IMAGE(定义图像对象)类的对象,在初始化函数中将图片加载进去,为了好看一点,我们把界面的颜色设置成白色,这时候不会有任何现象出现,下来我们将蛇头蛇身都换成图片,我们直到我们没有办法将蛇头在程序中转向90度,所以我们准备四张蛇头的图片,分别对应上下左右四个方向,然后用一个switch当前的蛇头的方向是哪一个就把哪一个方向的蛇头放在蛇头的位置,图片的尺寸也要和蛇头的尺寸相同,然后开始绘制蛇身,蛇身不用那么多情况,直接将蛇身的图片放在蛇身的位置就可以了。下面就是绘制食物了,将食物的图片放在食物的位置即可。
我们再来运行一下:
这里写图片描述
我们介绍一下,加入图片的几个函数,
(1)loadimage()加载图片,一共四个参数,第一个参数是IMAGE类的对象的地址,第二个是图片的名称,是一个字符串(L是编码格式的转换),剩下两个参数表示图片的尺寸
(2)putimage()放置图片到某个位置,一共三个参数,放入位置的横座标,放入位置的纵座标,IMAGE类的对象的地址
 下面我们为这个游戏再加上背景音乐:
使用PlaySound函数,一共三个参数,第一个是歌曲路径,第二个参数是应用程序的一个实例句柄,除非指向一个资源标识符,否则设置为空,第三个参数播放的方式,使用这个函数时要加上:#pragma comment(lib, "winmm.lib")
否则无法使用。而且这个函数只能播放wav格式的音乐,可以使用格式工厂将mp3格式转化为wav格式再使用。
这里写图片描述
代码如下:

#include<graphics.h>//图形库头文件
#include<conio.h>//按键相关的头文件
#include<time.h>
#include<imapi.h>
#include<windows.h>
#include<mmsystem.h>
#pragma comment(lib,"winmm.lib")
#define MAX 1000//蛇的最大节数
#define SIZE 20//蛇的尺寸
IMAGE sheadup, sheaddown, sheadleft, sheadright,body, sfood;

struct COOR//座标类
{
    int x;
    int y;
};
enum CH
{
    up=72,//键码值
    down=80,
    left=75,
    right=77
};
//蛇类
struct SNAKE
{
    int n;//蛇的节数
    COOR scr[MAX];//蛇的座标
    CH ch;//蛇的方向
}snake;

struct FOOD//食物类    
{
    COOR fcr;//食物座标
    int  flag;//食物是否生成的标志
}food;

void initGame()
{
    initgraph(640, 480);//窗口宽度,高度

    setbkcolor(WHITE);
    cleardevice();
    //加载图片
    loadimage(&sheadup, L"蛇头上.jpg", SIZE, SIZE);
    loadimage(&sheaddown, L"蛇头下.jpg", SIZE, SIZE);
    loadimage(&sheadleft, L"蛇头左.jpg", SIZE, SIZE);
    loadimage(&sheadright, L"蛇头右.jpg", SIZE, SIZE);
    loadimage(&body, L"蛇身.jpg", SIZE, SIZE);
    loadimage(&sfood, L"西瓜.jpg", SIZE, SIZE);
    snake.n = 1;//蛇的节数初始化
    snake.scr[0].x = 0;//蛇的座标
    snake.scr[0].y = 0;
    snake.ch = right;

    food.flag = 0;//生成食物
}

//绘制蛇
void SnakePaint()
{
    //绘制蛇头
    /*fillrectangle(snake.scr[0].x, snake.scr[0].y, snake.scr[0].x + SIZE, \
        snake.scr[0].y + SIZE);*/
    switch (snake.ch)
    {
    case up:
        putimage(snake.scr[0].x, snake.scr[0].y, &sheadup);
        break;
    case down:
        putimage(snake.scr[0].x, snake.scr[0].y, &sheaddown);
        break;
    case left:
        putimage(snake.scr[0].x, snake.scr[0].y, &sheadleft);
        break;
    case right:
        putimage(snake.scr[0].x, snake.scr[0].y, &sheadright);
        break;
    default:
        break;
    }
    //绘制蛇身
    for (int i = snake.n - 1; i > 0; i--)
    {
        /*fillrectangle(snake.scr[i].x, snake.scr[i].y, snake.scr[i].x + \
            SIZE, snake.scr[i].y + SIZE);*/
        putimage(snake.scr[i].x, snake.scr[i].y, &body);
    }
}
//蛇的移动
void SnakeMove()
{
    for (int i = snake.n - 1; i > 0; i--)
    {
        snake.scr[i].x = snake.scr[i - 1].x;
        snake.scr[i].y = snake.scr[i - 1].y;
    }
    switch (snake.ch)
    {
    case up:
        snake.scr[0].y -= SIZE;
        break;
    case down:
        snake.scr[0].y += SIZE;
        break;
    case left:
        snake.scr[0].x -= SIZE;
        break;
    case right:
        snake.scr[0].x += SIZE;
        break;
    default:
        break;
    }
}
//控制蛇的方向
void ChangeSnakech()
{
    switch (_getch())
    {
    case 72:
        if (snake.ch != down)
            snake.ch = up;
        break;
    case 80:
        if (snake.ch != up)
            snake.ch = down;
        break;
    case 75:
        if (snake.ch != right)
            snake.ch = left;
        break;
    case 77:
        if (snake.ch != left)
            snake.ch = right;
        break;
    default:
        break;
    }
}
//生成食物座标
void FoodCoor()
{
    //随机数种子
    srand((unsigned)time(NULL));
    food.fcr.x = rand() % 32 * SIZE;
    food.fcr.y = rand() % 24 * SIZE;
    food.flag = 1;//生成座标以后先不生成食物

}
//绘制食物
void FoodPaint()
{
    /*setlinecolor(BLUE);
    roundrect(food.fcr.x,food.fcr.y,\
        food.fcr.x+SIZE,food.fcr.y+SIZE,10,10);*///绘制圆角矩形
    putimage(food.fcr.x, food.fcr.y, &sfood);
}

//吃食物
void EatFood()
{
    if (snake.scr[0].x == food.fcr.x&&snake.scr[0].y == food.fcr.y)
    {
        snake.n++;
        food.flag = 0;
    }
}

void judge()
{
    cleardevice();
    settextcolor(LIGHTGREEN);
    settextstyle(50, 0, L"宋体");
    outtextxy(200, 200, L"Game Over");

    Sleep(3000);
    exit(0);
}
//判断游戏结束
void GameOver()
{
    //蛇头撞到了墙
    if (snake.scr[0].x<0 || snake.scr[0].x>620 ||\
        snake.scr[0].y<0 || snake.scr[0].y>460)
    {
        judge();
    }
    //蛇头撞到了蛇身
    for (int i = snake.n - 1; i > 0; i--)
    {
        if (snake.scr[0].x == snake.scr[i].x\
            &&snake.scr[0].y == snake.scr[i].y)
        {
            judge();
        }
    }
}
int main()
{

    initGame();

    PlaySound(L"F:\\Documents\\工程\\贪吃蛇\\贪吃蛇\\PGL - Dear mom.wav", NULL, SND_FILENAME | SND_ASYNC | SND_LOOP);

    while (1)
    {
        while (!_kbhit())//当按下按键退出循环
        {
            if (food.flag == 0)
                FoodCoor();
            cleardevice();//刷新屏幕
            FoodPaint();
            SnakeMove();//蛇的移动
            SnakePaint();//蛇的绘制
            Sleep(300);
            EatFood();
            GameOver();
        }

        ChangeSnakech();
    }

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章