贪吃蛇设计含文档

链队列的应用——贪吃蛇游戏设计

1, 贪吃蛇游戏设计描述

     链队列这种数据结构的特点是数据先进先出,利用这个特点我们可以用来模拟贪吃蛇,来进行贪吃蛇游戏的开发。蛇的游动插入头节点,删除尾结点。游戏界面的围墙,可以控制控制台的光标在界面打印一个长方形的围墙表示游戏围墙。

2,链队列表的ADT描述及贪吃蛇相关函数功能模块

  • 光标的移动:  Goto(x,y)x,y表示界面要移动光标的位置
  • 围墙的显示:    Print_map()
  • 随机创造食物:creat_food()
  • 初始化蛇的身体:Insert_snack()
  • 蛇的移动:      Move_snack(before_x,before_y)

Operate_order()

3, 贪吃蛇相关函数功能的伪代码

struct Snack

{

    int x;

    int y;

    struct Snack* next;

};

struct Food

{

    int x;

    int y;

};

struct Snack* snack;//声明蛇的头部

struct Snack* front, * rear;//分别表示蛇头,蛇尾

struct Food food;//声明食物

int Width = 40, Length = 40,judge1=0,score=0;//依次表示围墙的宽,长度,是否吃到食物的标志,得分//光标的移动,控制围墙的打印,每次移动蛇头的打印

void Goto(x, y)//x,y代表界面的光标的坐标

{

    COORD coord;

    coord.X = x, coord.Y = y;

    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);//将光标移动到指定位置

    CONSOLE_CURSOR_INFO cursor_info = { 1,0 };//游标信息

    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);//设置控制台光标信息,隐藏光标

}//打印地图和出身蛇的身体,goto(x,y)函数,将界面的光标移动到指定位置打印方块围墙

void Print_map()

{

    for (i = 0; i < Length/2; i++)cout << "■";//Length表示围墙的长度,由于方块占界面两个位置,Length要除2

    for (i = 1; i < Width/2-1; i++)//Width表示围墙的宽

    {

        Goto(0,i);//移动光标

        cout << "■";//打印围墙

    }

    for (i = 1; i < Width/2 - 1; i++)

    {

        Goto(Length-2, i);

        cout << "■";

    }

    Goto(0,Width/2-1);

    for (i = 0; i < Length / 2; i++)cout << "■";

    Goto(snack->x, snack->y);//把光标移动到蛇头的位置

    cout << "●";//打印蛇头

    p = front->next;

    while (p != NULL)Goto(p->x, p->y), cout << "◇", p = p->next;//打印蛇身

}

//随机创造食物

void creat_food()

{

    p = front;//创建辅助变量

    while (1)

    {

        srand((int)time(0));//建立随机数种子

        food.x = rand() % 35 + 2, food.y = rand() % 18 + 1;//创建食物坐标,确保坐标在围墙内

        //由于食物图像也占两个坐标,而蛇头在x轴上的移动是每两个进行移动,所以创建的食物坐标在x轴上需为偶数,否则蛇永远吃不到食物

        if (food.x % 2 != 0)food.x++;

        while (p!=NULL&&food.x != p->x && food.y != p->y)p = p->next;//判断创建的食物是否与蛇身重合

        if (p==NULL)//如果不重合,则p==NULL,跳出循环

        {

             break;

        }

    }

    Goto(food.x, food.y);//打印食物

    cout << "★";//星号代表食物

}

//初始化蛇的身体

void Insert_snack()

{

    snack->x = 20, snack->y = 10, snack->next = NULL;//初始化蛇头

    front = snack;

    p = (struct Snack*)malloc(sizeof(struct Snack));//以下初始化蛇身

    snack->next = p, p->x = 22, p->y = 10;

    p->next= (struct Snack*)malloc(sizeof(struct Snack));

    p = p->next, p->x = 24, p->y = 10;

    p->next= (struct Snack*)malloc(sizeof(struct Snack));

    p = p->next, p->x = 26, p->y = 10, p->next = NULL, rear = p;

}

}//判断蛇头的每一次移动是否撞到墙壁

void crash_wall()

{

    if (front->y == 0 || front->y == 19 || front->x <= 1 || front->x >= 38)//由于围墙的每一个方块在x轴上占两个坐标,所以要小于等于1,大于等于38

    {

        Goto(22, 22);

        cout << "游戏结束";

        exit(0);

        return ;

    }

}

//蛇身的移动

void move_snack()

{

    Goto(front->x, front->y);//将光标移动到最新的蛇头

    cout << "●";//打印蛇头

    Goto(front->next->x, front->next->y);//原来的蛇头变成蛇身

    cout << "◇";

    Goto(45, 9);

    cout << "当前得分:" << score;//打印当前得分

    if (judge1 ==1)//如果吃到食物,则重新创造食物,此函数结束,否则,将蛇尾删除

    {

        creat_food();

        return;

    }

    Goto(rear->x,rear->y);

    p=front; //做辅助变量

    while (p->next!= rear)p = p->next;

    rear = p;

    free(p->next);//释放蛇尾的内存,删除蛇尾

    rear->next = NULL;

    cout << "  ";

}

//判断是否吃到食物

void eat_food()

{

    if (front->x == food.x && front->y == food.y)//如果蛇头坐标等于食物坐标,则吃到食物

    {

        judge1 = 1;//标志吃到食物

        score++;//分数加一

    }

}

//接受键盘指令,指挥蛇的移动,键盘上的←↓↑→进行控制

void Operate_order()

{

    next_step->x = front->x, next_step->y = front->y; //创建下一步的结点

    if (GetAsyncKeyState(VK_UP))//如果指令为↑,蛇头的y坐标减一

    {

        next_step->y--, next_step->x = front->x;

    }

    else if (GetAsyncKeyState(VK_DOWN))//如果指令为↓,蛇头的y坐标加一

    {

        next_step->y++, next_step->x = front->x;

    }

    else if (GetAsyncKeyState(VK_LEFT))//如果指令为←,蛇头的x坐标减二,由于方块图形占x轴两个位置,所以减二

    {

        next_step->x -= 2, next_step->y = front->y;

    }

    else if (GetAsyncKeyState(VK_RIGHT))//如果指令为→,蛇头的x坐标加二,由于方块图形占x轴两个位置,所以加二

    {

        next_step->x += 2, next_step->y = front->y;

    }

    else

    {

        if (front->x == front->next->x)//如果没有接受移动指令,则沿当前方向移动

        {

             if (front->y > front->next->y)

             {

                 next_step->y++, next_step->x = front->x;

             }

             else

             {

                 next_step->y--, next_step->x = front->x;

             }

        }

        else if (front->x > front->next->x)

        {

             next_step->x += 2, next_step->y = front->y;

        }

        else

        {

             next_step->x -= 2, next_step->y = front->y;

        }

    }

    next_step->next = front, front = next_step;

    eat_food();//如果吃到食物,插入一个结点,judge1置为1,标志吃到食物

    move_snack();//移动蛇身

    crash_wall();//判断是否撞墙,撞墙就结束游戏

    judge1 = 0;//每一次蛇移动,是否吃到食物标志置为0

 

}

3,程序运行结果及截图

游戏刚开始界面

 

游戏中途界面

 

游戏结束界面

 

4,学习心得

            在编写时,比较苦难的地方在于,如何呈现围墙游戏界面,除了采用数组之外,我们可以采用SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord)这个函数,将界面数组化,提高程序运行的效率,减少游戏运行时的卡顿问题。除了采用这个函数,我们也可以采用清屏函数,但是做出来的游戏体验极差,蛇每走一步,图面就必须重新打印一边。

      此外,如何呈现蛇的移动这个问题,我们可以采用,蛇头移动一步,就打印,原来的蛇头打印成身体,末尾打印成空格,这样程序就可以呈现出蛇在移动的效果

5,附源代码(无)

#include<iostream>

#include<conio.h>

#include<ctime>

#include<Windows.h>

using namespace std;

struct Snack

{

    int x;

    int y;

    struct Snack* next;

};

struct Food

{

    int x;

    int y;

};

struct Snack* snack;//声明蛇的头部

struct Snack* front, * rear;//分别表示蛇头,蛇尾

struct Food food;//声明食物

int Width = 40, Length = 40,judge1=0,score=0;//依次表示围墙的宽,长度,是否吃到食物的标志,得分

//光标的移动,控制围墙的打印,每次移动蛇头的打印

void Goto(int x, int y)//x,y代表界面的光标的坐标

{

    COORD coord;

    coord.X = x, coord.Y = y;

    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);//将光标移动到指定位置

    CONSOLE_CURSOR_INFO cursor_info = { 1,0 };//游标信息

    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);//设置控制台光标信息,隐藏光标

}

//随机创造食物

void creat_food()

{

    struct Snack* p = front;//创建辅助变量

    while (1)

    {

        srand((int)time(0));//建立随机数种子

        food.x = rand() % 35 + 2, food.y = rand() % 18 + 1;//创建食物坐标,确保坐标在围墙内

        //由于食物图像也占两个坐标,而蛇头在x轴上的移动是每两个进行移动,所以创建的食物坐标在x轴上需为偶数,否则蛇永远吃不到食物

        if (food.x % 2 != 0)food.x++;

        while (p!=NULL&&food.x != p->x && food.y != p->y)p = p->next;//判断创建的食物是否与蛇身重合

        if (p==NULL)//如果不重合,则p==NULL,跳出循环

        {

            break;

        }

    }

    Goto(food.x, food.y);//打印食物

    cout << "★";//星号代表食物

}

//判断是否吃到食物

void eat_food()

{

    if (front->x == food.x && front->y == food.y)//如果蛇头坐标等于食物坐标,则吃到食物

    {

        judge1 = 1;//标志吃到食物

        score++;//分数加一

    }

}

//初始化蛇的身体

void Insert_snack()

{

    snack->x = 20, snack->y = 10, snack->next = NULL;//初始化蛇头

    front = snack;

    struct Snack* p;

    p = (struct Snack*)malloc(sizeof(struct Snack));//以下初始化蛇身

    snack->next = p, p->x = 22, p->y = 10;

    p->next= (struct Snack*)malloc(sizeof(struct Snack));

    p = p->next, p->x = 24, p->y = 10;

    p->next= (struct Snack*)malloc(sizeof(struct Snack));

    p = p->next, p->x = 26, p->y = 10, p->next = NULL, rear = p;

}

//打印地图和出身蛇的身体,goto(x,y)函数,将界面的光标移动到指定位置打印方块围墙

void Print_map()

{

    for (int i = 0; i < Length/2; i++)cout << "■";//Length表示围墙的长度,由于方块占界面两个位置,Length要除2

    for (int i = 1; i < Width/2-1; i++)//Width表示围墙的宽

    {

        Goto(0,i);//移动光标

        cout << "■";//打印围墙

    }

    for (int i = 1; i < Width/2 - 1; i++)

    {

        Goto(Length-2, i);

        cout << "■";

    }

    Goto(0,Width/2-1);

    for (int i = 0; i < Length / 2; i++)cout << "■";

    Goto(snack->x, snack->y);//把光标移动到蛇头的位置

    cout << "●";//打印蛇头

    struct Snack* p = front->next;

    while (p != NULL)Goto(p->x, p->y), cout << "◇", p = p->next;//打印蛇身

}

//蛇身的移动

void move_snack()

{

    Goto(front->x, front->y);//将光标移动到最新的蛇头

    cout << "●";//打印蛇头

    Goto(front->next->x, front->next->y);//原来的蛇头变成蛇身

    cout << "◇";

    Goto(45, 9);

    cout << "当前得分:" << score;//打印当前得分

    if (judge1 ==1)//如果吃到食物,则重新创造食物,此函数结束,否则,将蛇尾删除

    {

        creat_food();

        return;

    }

    Goto(rear->x,rear->y);

    struct Snack*p=front;

    while (p->next!= rear)p = p->next;

    rear = p;

    free(p->next);//释放蛇尾的内存

    rear->next = NULL;

    cout << "  ";

}

//判断蛇头的每一次移动是否撞到墙壁

void crash_wall()

{

    if (front->y == 0 || front->y == 19 || front->x <= 1 || front->x >= 38)//由于围墙的每一个方块在x轴上占两个坐标,所以要小于等于1,大于等于38

    {

        Goto(45, 15);

        cout << "游戏结束";

        Goto(2, 25);

        exit(0);

        return ;

    }

}

//接受键盘指令,指挥蛇的移动,键盘上的←↓↑→进行控制

void Operate_order()

{

    struct Snack* next_step = (struct Snack*)malloc(sizeof(struct Snack));//创建下一步的结点

    next_step->x = front->x, next_step->y = front->y;

    if (GetAsyncKeyState(VK_UP))//如果指令为↑,蛇头的y坐标减一

    {

        next_step->y--, next_step->x = front->x;

    }

    else if (GetAsyncKeyState(VK_DOWN))//如果指令为↓,蛇头的y坐标加一

    {

        next_step->y++, next_step->x = front->x;

    }

    else if (GetAsyncKeyState(VK_LEFT))//如果指令为←,蛇头的x坐标减二,由于方块图形占x轴两个位置,所以减二

    {

        next_step->x -= 2, next_step->y = front->y;

    }

    else if (GetAsyncKeyState(VK_RIGHT))//如果指令为→,蛇头的x坐标加二,由于方块图形占x轴两个位置,所以加二

    {

        next_step->x += 2, next_step->y = front->y;

    }

    else

    {

        if (front->x == front->next->x)//如果没有接受移动指令,则沿当前方向移动

        {

             if (front->y > front->next->y)

             {

                 next_step->y++, next_step->x = front->x;

             }

             else

             {

                 next_step->y--, next_step->x = front->x;

             }

        }

        else if (front->x > front->next->x)

        {

             next_step->x += 2, next_step->y = front->y;

        }

        else

        {

             next_step->x -= 2, next_step->y = front->y;

        }

    }

    next_step->next = front, front = next_step;

    eat_food();//如果吃到食物,插入一个结点,judge1置为1,标志吃到食物

    move_snack();//移动蛇身

    crash_wall();//判断是否撞墙,撞墙就结束游戏

    judge1 = 0;//每一次蛇移动,吃到食物标志置为0

}

void stop()

{

    int a = 0;

    if (GetAsyncKeyState(VK_SPACE))

    {

        a = 1;

    }

    while (a)

    {

        if (GetAsyncKeyState(VK_SPACE))

        {

             a = 0;

             break;

        }

    }

}

int main()

{

    snack = (struct Snack*)malloc(sizeof(struct Snack));

    Goto(45, 6);

    cout << "键盘上←↑→↓控制游戏,点击空格开始游戏";

    while (1)

    {

        if (GetAsyncKeyState(VK_SPACE))

        {

             break;

        }

    }

    Goto(0,0);

    Insert_snack();

    Print_map();

    creat_food();

    while (1)

    {

        Operate_order();

        Sleep(250);

        stop();//按空格暂停游戏,再次按空格开始游戏

    }

 

}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值