贪吃蛇游戏设计总结

贪吃蛇游戏设计总结

程序设计

模块化

贪吃蛇游戏可大致分为三部分:
    1.打印地图、蛇、果子;
    2.蛇:移动、转向;
    3.果子;

蛇的活动是这个程序的主要部分、其次是如何打印画面。

  • 从数据结构方面考虑,蛇方面目前我分别使用了数组、链表制作了游戏。将蛇想象成由一个个节点构成的对象,那么只需要考虑蛇头也就是第一个节点的移动,其余节点各是前一个节点的复制。打印、移动、打印如此循环往复,形成动画。
    • 二维数组:将二维数组视作坐标系,可选用字符或者数字实现,蛇头节点是一个数字代表蛇的长度,每次移动,将这个特殊的数字赋给下一个坐标,然后打印,再对二维数组全部进行一次-1直到0。(例如打印3,减一->2 移动->2-3、打印、减一->1-2 、 移动->1-2-3 打印 循环反复就能实现一串递增的数字在屏幕上移动 )
    • 链表:链表本身就是如同蛇一样的结构,每次只需要改变第一个节点的坐标,再让后面的每一个节点的坐标等于先前前面每一个节点的左边,只需要使用gotoxy函数改变光标的位置依次打印每一个节点就可以实现移动。
    • 转向:检测是否有键盘输入,如果没有则按照当前方向前进,如果有则判断是否符合运动逻辑(比如往前走,不能直接往后走),如果符合就把这个更改方向并把方向存储起来。代码里则是改变第一个节点的坐标后面节点复制前面节点实现转向。
  • 果子
    • 如果没有果子,则随机生成一个坐标,在这个坐标放置一个标志,在蛇运动代码中判断蛇头坐标是否等于果子坐标。
  • 打印
    • 打印-清除(system("cls"))-打印 重复实现动画
    • 如果采取printf方式打印会出现闪屏情况,是由于屏幕刷新太快,而图案是一个个打印,所以就出现闪屏现象
    • 改用双缓冲方式打印,原理是创建两个缓冲区,通过更换缓冲区实现打印。循环:将图案打印在非活动的缓冲区,再将这个缓冲区设置为活动缓冲区,这样每次打印的就是一个完整的图案,就不会有闪屏现象。(数组不需要)https://blog.csdn.net/weixinhum/article/details/72179593

代码

数组实现
点击查看代码
#include <stdio.h>
#include <windows.h>
#include <conio.h>
#include <time.h>
#include <stdlib.h>
void print1(int num[10][10])
{
    system("cls");
    int i, j;
    for (i = 0; i < 10; i++)
    {
        for (j = 0; j < 10; j++)
        {
            printf(" %2d", num[i][j]);
        }
        printf("\n");
    }
}

int main()
{
    int x1, y1;
    srand((unsigned)time(NULL));
    int num[10][10] = {0};
    int snakehead = 3;
    num[5][5] = snakehead;
    int x = 5;
    int y = 5;
    print1(num);
    int flag = 0;
    char direction = 'd';
    int score = 0;
    int flag1 = 0;
    while (flag == 0)
    {
        if (flag1 == 0)
        {
            flag1 = 1;
            do
            {
                x1 = rand() % 10 + 1;
                y1 = rand() % 10 + 1;
            } while (num[x1][y1] != 0);
            num[x1][y1] = 99;
        }
        if (kbhit()) // kbhit 用于检测键盘输出的函数
        {
            char turn = getch();
            if (direction == 'd' || direction == 'a')
            {
                if (turn == 'w' || turn == 's')
                    direction = turn;
            }
            if (direction == 'w' || direction == 's')
            {
                if (turn == 'a' || turn == 'd')
                    direction = turn;
            }
        }
        else
        {
            if (direction == 'W' || direction == 'w')
            {
                if (x == 0)
                    x = 9;
                else
                    x--;
            }
            if (direction == 'a' || direction == 'A')
            {
                if (y == 0)
                    y = 9;
                else
                    y--;
            }
            if (direction == 's' || direction == 'S')
            {
                if (x == 9)
                    x = 0;
                else
                    x++;
            }
            if (direction == 'd' || direction == 'D')
            {
                if (y == 9)
                    y = 0;
                else
                    y++;
            }
            if (num[x][y] == 99)
            {
                score++;
                flag1 = 0;
                snakehead++;
            }
            if (num[x][y] != 0 && num[x][y] != 99)
            {
                flag = 1;
                Sleep(1000);
            }

            num[x][y] = snakehead;
            print1(num);
        }
        for (int i = 0; i < 10; i++)
        {
            for (int j = 0; j < 10; j++)
            {
                if (num[i][j] != 0 && num[i][j] != 99)
                {
                    num[i][j]--;
                }
            }
        }
        Sleep(100);
    }
    system("cls");
    printf("你的得分是:%d", score);
    return 0;
}

链表实现(在舍友的电脑上运行看不到蛇,不知道是不是windows系统不同的问题  
点击查看代码
#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
#include <conio.h>
#include <time.h>
#include <malloc.h>
#define _Y 20      // 15行
#define _X 100     // 20列
char data[_Y][_X]; //这是全局变量定义的字符数组

HANDLE hOutput, hOutBuf; //控制台屏幕缓冲区句柄
HANDLE houtpoint;
COORD coord = {5, 0}; //初始输出位置
DWORD bytes = 0;
int hop_flag = 0; //通过指针轮流指向两个缓冲区,实现双缓冲
typedef struct snake_xy
{
    int x;
    int y;
} xy;
struct Node //节点
{
    xy xy1;
    struct Node *next;
};

void begin();
void menu(int *);
void snake(int a, int *b);
void change(int *);
void background(int x, int y);
void gotoxy(int x, int y);
struct Node *newNode(xy n);
struct Node *creatlist();
void insertNode_to_head(struct Node *snakehead, xy xy2);
void freeList(struct Node *headNode);
void printList(struct Node *headnode, int score, int level);
void fruits(int *flag, xy *fruit);
void move(struct Node *headNode, char direction, int level);
void insertnewNode_to_back(struct Node *headnode);

void printPic()
{
    int i, j;
    hop_flag = !hop_flag;
    if (!hop_flag)
    { //这里是每次交替,直接把hOutput或hOutBuf赋给houtpoint
        houtpoint = hOutput;
    }
    else
    {
        houtpoint = hOutBuf;
    }

    for (i = 0; i < _Y; i++)
    { //打印你需要的二维数组图案
        for (j = 0; j < _X; j++)
        {
            if (i == 0 || i == _Y - 1 || j == 0 || j == _X - 1)
            {
                data[i][j] = '*';
            }
            else
            {
                data[i][j] = ' ';
            }
        }
    }
    coord.Y = 1;
    for (i = 0; i < _Y; i++)
    {              //循环打印每一行
        coord.Y++; //每次都打印到下一行
        WriteConsoleOutputCharacter(houtpoint, data[i], _X, coord, &bytes);
    } // data[i]:每行的地址。 _X: 每行的长度

    SetConsoleActiveScreenBuffer(houtpoint);
}

int main()
{
    hOutBuf = CreateConsoleScreenBuffer(
        GENERIC_WRITE,
        FILE_SHARE_WRITE,
        NULL,
        CONSOLE_TEXTMODE_BUFFER,
        NULL);
    hOutput = CreateConsoleScreenBuffer(
        GENERIC_WRITE,
        FILE_SHARE_WRITE,
        NULL,
        CONSOLE_TEXTMODE_BUFFER,
        NULL);
    srand((unsigned)time(NULL));
    begin();
    system("cls");
    srand(time(NULL));
    int p = 1;
    int level = 1;
    int on = 0;
    printf(" 按w/s选择,输入a确认\n\n");
    Sleep(2000);
    while (on == 0)
    {
        if (p == 1)
        {
            menu(&p); //菜单
        }
        switch (p)
        {
        case 0:
            snake(level, &on);
            break;
        case 1:
            change(&level);
            system("cls");
            break;
        case 2:
            return 0;
            break;
        default:
            break;
        }
    }
    return 0;
}

void begin()
{
    int i = 0;
    for (i = 0; i < 30; i++)
    {
        printf("*");
    }
    printf("\n\n");
    printf("\t 贪吃蛇游戏\n\n");
    for (i = 0; i < 30; i++)
    {
        printf("*");
    }
    Sleep(1500);
}
void menu(int *select)
{
    int a = 0;
    char b = 0;
    int c = 0;
    system("cls");
    system("color 6");
    system("cls");
    printf("\n\n");
    while (c == 0)
    {
        int i = 0;
        system("cls");
        system("color 6");
        gotoxy(5, 0);
        switch (a)
        {
        case 0:
            printf("\t  开始游戏\n");
            break;
        case 1:
            printf("\t  难度设置\n");
            break;
        case 2:
            printf("\t  退出游戏\n");
            break;
        default:
            break;
        }
        b = getch();
        switch (b)
        {
        case 'w':
            if (a == 0)
                a = 2;
            else
                a--;
            break;
        case 's':
            if (a == 2)
                a = 0;
            else
                a++;
            break;
        case 'a':
            c = 1;
            break;
        default:
            break;
        }
    }
    *select = a;
}
void change(int *level)
{
    int a = 1;
    printf("\t 当前难度:%d \n", *level);
    printf("\t 请输入变更后的难度:>(1-5):\n");
    printf("\t");
    scanf("%d", &a);
    getchar();
    *level = a;
}
void snake(int level, int *on)
{
    xy fruit; //果子坐标
    char direction = 'd';
    int flag = 0;  //碰墙
    int flag1 = 0; //果子
    int score = 0;
    char flag3 = 0; //是否继续
    struct Node *list = creatlist();
    xy xys = {50, 10};
    struct Node *snakehead = newNode(xys);
    list->next = snakehead;
    xys.x = 49;
    insertNode_to_head(snakehead, xys);
    xys.x = 48;
    insertNode_to_head(snakehead, xys);
    Sleep(2000);
    system("cls");
    fflush(stdin);
    printList(list, score, level);
    fruits(&flag1, &fruit);
    while (flag == 0)
    {
        fflush(stdin);
        if (kbhit()) // kbhit 用于检测键盘输出的函数
        {
            char turn = getch();
            if (direction == 'd' || direction == 'a')
            {
                if (turn == 'w' || turn == 's')
                    direction = turn;
            }
            if (direction == 'w' || direction == 's')
            {
                if (turn == 'a' || turn == 'd')
                    direction = turn;
            }
        }
        else
        {
            move(list, direction, level);
            printList(list, score, level);
            fruits(&flag1, &fruit);
        }
        if (snakehead->xy1.x == 4 || snakehead->xy1.x == 104 || snakehead->xy1.y == 2 || snakehead->xy1.y == 21)
        {
            flag = 1;
            gotoxy(50, 10);
            printf("你输了");
            printf("是否重新开始?:>(Y/N)");
            scanf("%c", &flag3);
            if (flag3 == 'N' || flag3 == 'n')
            {
                *on = 1;
                freeList(list);
                return;
            }
            system("cls");
        }
        if (snakehead->xy1.x == fruit.x && fruit.y == snakehead->xy1.y)
        {
            score++;
            flag1 = 0;
            insertnewNode_to_back(list);
        }
    }
}
void gotoxy(int x, int y) //移动光标
{
    COORD p;
    p.X = x;
    p.Y = y;
    SetConsoleCursorPosition(houtpoint, p); //移动光标
}
struct Node *creatlist() //创建链表
{
    struct Node *headnode = (struct Node *)malloc(sizeof(struct Node));
    headnode->next = NULL;
    return headnode;
}
struct Node *newNode(xy n) //新建节点
{
    struct Node *newNode1 = (struct Node *)malloc(sizeof(struct Node));
    newNode1->xy1 = n;
    newNode1->next = NULL;
    return newNode1;
}
void printList(struct Node *headnode, int score, int level) //打印链表
{
    system("cls");
    printPic();
    struct Node *nowNode = headnode->next; // snakehead
    while (nowNode != NULL)
    {
        int x = nowNode->xy1.x;
        int y = nowNode->xy1.y;
        gotoxy(x, y);
        printf("*");
        nowNode = nowNode->next;
    }
    gotoxy(105, 10);
    printf("your score:%d", score);
    gotoxy(105, 11);
    printf("当前的难度是:%d\n", level);
    gotoxy(105, 12);
    printf("用WASD进行移动");
}
void insertNode_to_head(struct Node *snakehead, xy xy2) //头部插入节点
{
    struct Node *newNode = (struct Node *)malloc(sizeof(struct Node));
    newNode->xy1 = xy2;
    newNode->next = snakehead->next;
    snakehead->next = newNode;
}
void freeList(struct Node *headNode) //释放链表内存
{
    struct Node *pwdNode = headNode;
    struct Node *pwdNodeback = pwdNode->next;
    while (pwdNode)
    {
        pwdNode = pwdNodeback;
        pwdNodeback = pwdNodeback->next;
        free(pwdNode);
    }
}
void move(struct Node *headNode, char direction, int level)
{
    struct Node *nowNode = headNode->next;
    struct Node *nowNode1 = (struct Node *)malloc(sizeof(struct Node *));
    nowNode1->xy1 = nowNode->xy1;
    struct Node *nowNode2 = (struct Node *)malloc(sizeof(struct Node *));
    nowNode2->xy1 = nowNode->next->xy1;
    switch (direction)
    {
    case 'd':
        nowNode->xy1.x = nowNode->xy1.x + 1;
        Sleep(110 - level * 10);
        break;
    case 'a':
        nowNode->xy1.x = nowNode->xy1.x - 1;
        Sleep(110 - level * 10);
        break;
    case 's':
        nowNode->xy1.y = nowNode->xy1.y + 1;
        Sleep(110 - level * 10);

        break;
    case 'w':
        nowNode->xy1.y = nowNode->xy1.y - 1;
        Sleep(110 - level * 10);
        break;
    default:
        break;
    }
    while (nowNode->next != NULL) // B = A1 A1->C1 C1 = B1
    {
        nowNode2->xy1 = nowNode->next->xy1;
        nowNode->next->xy1 = nowNode1->xy1;
        nowNode1->xy1 = nowNode2->xy1;
        nowNode = nowNode->next;
    }
}
void fruits(int *flag, xy *fruit)
{
    if (*flag == 0)
    {
        *flag = 1;
        (*fruit).x = 6 + rand() % 96 + 1;
        (*fruit).y = 4 + rand() % 16 + 1;
    }
    gotoxy((*fruit).x, (*fruit).y);
    printf("*");
}
void insertnewNode_to_back(struct Node *headnode) //尾插法
{
    struct Node *pwdNode = headnode->next;
    struct Node *NewNode = (struct Node *)malloc(sizeof(struct Node *));
    while (pwdNode->next != NULL)
    {
        pwdNode = pwdNode->next;
    }
    pwdNode->next = NewNode;
    NewNode->next = NULL;
}

运行结果


posted @ 2022-10-16 11:21  瞻鹤  阅读(717)  评论(0编辑  收藏  举报