关于游戏开发:代码解析双向链表实现贪吃蛇游戏简单易学开发自己第一个游戏

92次阅读

共计 3347 个字符,预计需要花费 9 分钟才能阅读完成。

如何利用双向链表实现一个繁难的 C 语言版贪吃蛇游戏(如下图所示)。

其中,黄色框代表贪吃蛇,红色 ★ 代表食物!

应用双向链表实现此游戏,有以下几点须要做重点剖析。

1) 
咱们晓得,双向链表中各个节点的规范形成是一个数据域和 2 个指针域,但对于实现贪吃蛇游戏来说,因为各个节点的地位是随贪吃蛇的挪动而变动的,因而链表中的各节点还须要随时进行定位。

在一个二维画面中,定义一个节点的地位,至多须要所在的行号和列号这 2 个数据。

由此,咱们能够得出形成贪吃蛇的双向链表中各节点的形成:

// 创立示意蛇各个节点的构造体
 
typedef struct SnakeNode {
 
    int x, y;// 记录节点所在的行和列
 
    struct SnakeNode *pre;// 指向前驱节点的指针
 
    struct SnakeNode *next;// 指向后续节点的指针
 
}Node, *pNode;

2) 
贪吃蛇的挪动,实质上就是对链表中各个节点的从新定位。

换句话说,除非贪吃蛇吃到食物,否则无论怎样挪动,都不会对双向链表的整个构造(节点数)产生影响,惟一受影响的就只是各个节点中 (x,y) 这对定位数据。

由此,咱们能够试着设计出实现贪吃蛇挪动的性能函数,本节所用的实现思维分为 2 步:

        ▶ 从蛇尾(双向链表尾节点)开始,挪动向前遍历,过程中顺次将以后节点的 (x,y) 批改为前驱节点的 (x,y),由此可实现整个蛇身(除首元节点外的其它所有节点)的向前挪动;

        ▶ 接管用户输出的挪动指令,依据用户批示贪吃蛇向左、向右、向上还是向下挪动,首元节点中的 (x,y) 别离做 x-1、x+1、y-1 和 y+1 运算。

如下所示,move() 函数就实现了贪吃蛇的挪动:

// 贪吃蛇挪动的过程,即链表中所有节点从尾结点开始一一向前挪动一个地位
 
bool Move(pNode pHead, char key) {
 
    bool game_over = false;
 
    pNode pt = pTail;
 
    while (pt != pHead) { // 每个节点顺次向前实现蛇的挪动
 
        pt->x = pt->pre->x;
 
        pt->y = pt->pre->y;
 
        pt = pt->pre;
 
    }
 
    switch (key) {
 
        case'd': {
 
            pHead->x += 1;
 
            if (pHead->x >= ROW)
 
                game_over = true;
 
            break;
 
        }
 
        case'a': {
 
            pHead->x -= 1;
 
            if (pHead->x < 0)
 
                game_over = true;
 
            break;
 
        }
 
        case's': {
 
            pHead->y += 1;
 
            if (pHead->y >= COL)
 
                game_over = true;
 
            break;
 
        }
 
        case'w': {
 
            pHead->y -= 1;
 
            if (pHead->y < 0)
 
                game_over = true;;
 
            break;
 
        }
 
    }
 
    if (SnakeDeath(pHead))
 
        game_over = true;
 
    return game_over;
 
}

留神,此段代码中还调用了 SnakeDeath() 函数,此函数用于判断贪吃蛇挪动时是否撞墙、撞本身,如果是则游戏完结。

3) 
当贪吃蛇吃到食物时,贪吃蛇须要减少一截,其本质也就是双向链表减少一个节点。然而实现这个性能惟一的难点在于:如何为该节点初始化 (x,y)?

这次所设计的贪吃蛇游戏,针对此问题,提供了最简略的解决方案,就是不对新节点 x 和 y 做初始化。

要晓得,贪吃蛇是时刻挪动的,而在下面的 move() 函数中,会时刻修改贪吃蛇每个节点的地位,因而当为双向链表增加新节点后,只有贪吃蛇挪动一步,新节点的地位就会自行更正。

也就是说,贪吃蛇吃到食物的实现,就仅是给双向链表增加一个新节点。如下即为实现此性能的代码:

// 创立示意食物的构造体,其中只须要记录其所在的行和列
 
typedef struct Food {
 
    int x;
 
    int y;
 
}Food, *pFood;
 
// 吃食物,等同于链表中新增一个节点
 
pNode EatFood(pNode pHead, pFood pFood) {
 
    pNode p_add = NULL, pt = NULL;
 
    if (pFood->x == pHead->x&&pFood->y == pHead->y) {p_add = (pNode)malloc(sizeof(Node));
 
        score++;
 
        pTail->next = p_add;
 
        p_add->pre = pTail;
 
        p_add->next = NULL;
 
        pTail = p_add;
 
        // 查看食物是否呈现在蛇身的地位上
 
        do {*pFood = CreateFood();
 
        } while (FoodInSnake(pHead, pFood));
 
    }
 
    return pHead;
 
}

其中,Food 构造体用来示意食物,其外部仅蕴含可能定位食物地位的 (x,y) 即可。

另外,此段代码中,还调用了 FoodeInSnake() 函数,因为食物的地位是随机的,因而极有可能会和贪吃蛇重合,所以此函数的性能就是:如果重合,就从新生成食物。

FoodInSnake() 函数的实现很简略,这里不再赘述:

// 判断食物的呈现地位是否和蛇身重合
 
bool FoodInSnake(pNode pHead, pFood pFood) {
 
    pNode pt = NULL;
 
    for (pt = pHead; pt != NULL; pt = pt->next) {if (pFood->x == pt->x&&pFood->y == pt->y)
 
            return true;
 
    }
 
    return false;
 
}

4) 
贪吃蛇游戏界面的显示,最简略的制作方法就是:贪吃蛇每挪动一次,都革除屏幕并从新生成一次。

这样实现的问题在于,如果贪吃蛇的挪动速度过快,则整个界面在渲染的同时,会掺杂着光标,并且屏幕界面会频繁闪动。因而,在渲染界面时,有必要将光标暗藏起来,这须要用到 <windows.h> 头文件,实现代码如下:

// 暗藏光标
 
void gotoxy(int x, int y) {HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
 
    COORD pos;
 
    pos.X = x;
 
    pos.Y = y;
 
    SetConsoleCursorPosition(handle, pos);
 
}
 
void HideCursor() {CONSOLE_CURSOR_INFO cursor_info = { 1, 0};
 
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
 
}

同时,为了给整个界面渲染上色彩,也须要引入 <windows.h> 头文件,并应用如下函数:

void color(int m) {
 
    HANDLE consolehend;
 
    consolehend = GetStdHandle(STD_OUTPUT_HANDLE);
 
    SetConsoleTextAttribute(consolehend, m);
 
}

5)
须要留神的一点是,由此完结后,肯定要手动开释双向链表占用的堆空间:

// 退出游戏前,手动销毁链表中各个节点
 
void ExitGame(pNode *pHead)
 
{
 
    pNode p_delete = NULL, p_head = NULL;
 
    while (*pHead != NULL) {p_head = (*pHead)->next;
 
        if (p_head != NULL)
 
            p_head->pre = NULL;
 
        p_delete = *pHead;
 
        free(p_delete);
 
        p_delete = NULL;
 
        *pHead = p_head;
 
    }
 
}

解决以上问题之后,用双向链表实现贪吃蛇游戏,基本上就没有难点了。家人们可依据本节提供的实现思维,尝试独立实现。

正文完
 0