用C语言实现一个贪吃蛇游戏

在写这个小游戏之前,我们会用到以前没见过的几个函数,这些都是Win32 API里的函数,在MSDN中都可以找到。
这里写图片描述
这里写图片描述
大家都知道,我们在运行程序的时候,在运行结果的后边都会有光标闪烁,并且有一句话:按任意键继续,现在我们要出现游戏界面,就不希望这个光标出现在有些位置,所以我们可以利用函数,让光标出现在最下方,不影响界面美观。我们还可以通过函数控制控制台窗口的大小。
分析:
这里写图片描述
这里写图片描述
好了,我们已经把游戏整个过程分析清楚了,我们来看看每个模块的代码吧。
GameStart:

void GameStart(pSnake ps)
{
    assert(ps);
    //打印欢迎界面
    WelcomeToGame();

    //创建地图
    CreateMap();

    //初始化蛇
    InitSnake(ps);

    //创建食物
    CreateFood(ps);

    ps->_AddScore = 10;
    ps->_TotalScore = 0;
    ps->_SleepTime = 200;
    ps->_Dir = RIGHT;
    ps->_Status = OK;
}

GameRun:

void GameRun(pSnake ps)
{
    //确定方向
    do
    {
        if (GetAsyncKeyState(VK_UP) && ps->_Dir != DOWN)
        {
            ps->_Dir = UP;
        }
        else if (GetAsyncKeyState(VK_DOWN) && ps->_Dir != UP)
        {
            ps->_Dir = DOWN;
        }
        else if (GetAsyncKeyState(VK_LEFT) && ps->_Dir != RIGHT)
        {
            ps->_Dir = LEFT;
        }
        else if (GetAsyncKeyState(VK_RIGHT) && ps->_Dir != LEFT)
        {
            ps->_Dir = RIGHT;
        }
        else if (GetAsyncKeyState(VK_SPACE))//空格键暂停游戏
        {
            Pause();
        }
        else if (GetAsyncKeyState(VK_ESCAPE))//ESC退出游戏
        {
            ps->_Status = NORMAL_END;
            break;
        }
        else if (GetAsyncKeyState(VK_F1))//F1加速
        {
            if (ps->_SleepTime >= 40)//应该有一个极限,如果ps->SleepTime=0,就不休眠,太快了,没办法控制
            {
                ps->_SleepTime -= 20;//每次休眠时间减少20毫秒(速度加快)
                ps->_AddScore += 2;//越快得分越高,每次在原来的基础上加2
            }
        }
        else if (GetAsyncKeyState(VK_F2))//F2减速
        {
            if (ps->_SleepTime <= 300)//_AddScore的初始值是10,没慢一下减2,ps->SleepTime(初始值为100)加20,若_SleepScore>200,_AddScore的值就变为负值了,不合理
            {
                ps->_SleepTime += 20;
                ps->_AddScore -= 2;
            }
            if (ps->_SleepTime > 300)
            {
                ps->_AddScore = 1;
            }
        }
        //蛇的移动
        SnakeMove(ps);

        Sleep(ps->_SleepTime);//在蛇每移动一下要暂停(移动的慢一点,让肉眼可以看清它的移动,也来的急控制下一步的方向)一下,不然会一直停不下来

        //被墙撞死
        KillByWall(ps);

        //被自己撞死
        KillBySelf(ps);

    } while (ps->_Status == OK);

}

GameEnd:

void GameEnd(pSnake ps)
{
    SetPos(26, 14);
    if (ps->_Status == NORMAL_END)
    {
        printf("正常退出");
    }
    if (ps->_Status == KILL_BY_WALL)
    {
        printf("哎呀,撞墙了");
    }
    if (ps->_Status == KILL_BY_SELF)
    {
        printf("不好意思,你自杀了");
    }
    SetPos(80, 27);
    pSnakeNode cur = ps->_pSnake;
    while (cur)
    {
        pSnakeNode Del = NULL;
        Del = cur;
        cur = cur->next;
        free(Del);
        Del = NULL;
    }
    ps->_pSnake = NULL;
    free(ps->_pFood);
    ps->_pFood = NULL;
}

我们现在就来看看整体的代码吧:
Snake.h

#ifndef __SNAKE_H__
#define __SNAKE_H__

#include<stdio.h>
#include<Windows.h>
#include<time.h>
#include<assert.h>
#include<malloc.h>

#define WALL "◆"
#define FOOD "●"
#define INIT_X 20
#define INIT_Y 10

enum Direction
{
    UP=1,
    DOWN,
    LEFT,
    RIGHT
};

enum GameStatus
{
    OK,
    NORMAL_END,//正常结束
    KILL_BY_WALL,
    KILL_BY_SELF
};

typedef struct SnakeNode
{
    int x;
    int y;
    struct SnakeNode* next;
}SnakeNode,*pSnakeNode;

typedef struct Snake
{
    pSnakeNode _pSnake;//维护蛇身
    pSnakeNode _pFood;//维护食物
    int _TotalScore;//总分数
    int _AddScore;//增加的分数
    int _SleepTime;//休眠的时间
    enum Direction _Dir;
    enum GameStatus _Status;
}Snake,*pSnake;

void GameStart(pSnake ps);
void WelcomeToGame();
void SetPos(int x, int y);
void CreateMap();
void InitSnake(pSnake ps);
pSnakeNode BuyNode();
void CreateFood(pSnake ps);
void GameRun(pSnake ps);
void Pause();
void SnakeMove(pSnake ps);
int NextIsFood(pSnake ps, pSnakeNode pn);
void EatFood(pSnake ps, pSnakeNode pn);
void NoFood(pSnake ps, pSnakeNode pn);
void KillByWall(pSnake ps);
void KillBySelf(pSnake ps);
void PrintHelpIafo(pSnake ps);
void GameEnd(pSnake ps);

#endif //__SNAKE_H__

Snake.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"Snake.h"

void SetPos(int x, int y)
{
    COORD pos = { 0 };
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
    pos.X = x;
    pos.Y = y;
    SetConsoleCursorPosition(handle, pos);//设置标准输出上的光标位置为pos
}

void WelcomeToGame()
{
    //设置窗口大小
    system("mode con cols=100 lines=30");

    SetPos(50, 15);//设置光标(控制句柄位置)
    printf("欢迎进入贪吃蛇游戏");
    SetPos(50, 28);
    system("pause");
    system("cls");

    SetPos(50, 15);
    printf("用↑,↓,←,→控制蛇的运动方向,F1为加速,F2为减速");
    SetPos(50, 16);
    printf("温馨提示:加速得分更多哦");
    SetPos(50, 29);
    system("pause");
    system("cls");
}

void CreateMap()
{
    int i = 0;
    //上边界
    for (i = 0; i <= 58; i += 2)
    {
        SetPos(i, 0);
        printf(WALL);
    }

    //下边界
    for (i = 0; i <= 58; i += 2)
    {
        SetPos(i, 27);
        printf(WALL);
    }

    //左边界
    for (i = 1; i < 27; i++)
    {
        SetPos(0, i);
        printf(WALL);
    }

    //右边界
    for (i = 1; i < 27; i++)
    {
        SetPos(58, i);
        printf(WALL);
    }
}

pSnakeNode BuyNode()
{
    pSnakeNode cur;
    cur = (pSnakeNode)malloc(sizeof(SnakeNode));
    if (NULL == cur)
    {
        printf("创建结点失败");
        return NULL;
    }
    cur->next = NULL;
    cur->x = 0;
    cur->y = 0;
    return cur;
}

void InitSnake(pSnake ps)
{
    assert(ps);
    int i = 0;
    pSnakeNode first = BuyNode();
    pSnakeNode cur = NULL;
    first->x = INIT_X;
    first->y = INIT_Y;
    for (i = 0; i < 4; i++)
    {
        cur = BuyNode();
        cur->x = first->x + 2;//因为宽度为2
        cur->y = first->y;
        cur->next = first;
        first = cur;
    }
    while (cur)
    {
        SetPos(cur->x, cur->y);
        printf(FOOD);
        cur = cur->next;
    }
    //printf("\n");
    ps->_pSnake = first;
//  system("pause");
}

void CreateFood(pSnake ps)
{
    assert(ps);
    pSnakeNode cur = ps->_pSnake;
    pSnakeNode pfood = BuyNode();
    //保证食物坐标合法
    pfood->y = rand() % 26 + 1;
    do
    {
        pfood->x = rand() % 55 + 2;
    } while (pfood->x % 2 != 0);  //并且食物坐标不能在奇数列,因为食物和边界图形大小占2列,不然蛇永远吃不到食物

    //食物不能出现在蛇身上
    while (cur)
    {
        if (cur->x == pfood->x && cur->y == pfood->y)//如果食物出现在蛇身上,我们就再递归调用函数,重新创建食物 
        {
            CreateFood(ps);
            return;
        }
        cur = cur->next;
    }
    ps->_pFood = pfood;
    SetPos(pfood->x, pfood->y);
    printf(FOOD);
    //system("pause");
}

void GameStart(pSnake ps)
{
    assert(ps);
    //打印欢迎界面
    WelcomeToGame();

    //创建地图
    CreateMap();

    //初始化蛇
    InitSnake(ps);

    //创建食物
    CreateFood(ps);

    ps->_AddScore = 10;
    ps->_TotalScore = 0;
    ps->_SleepTime = 200;
    ps->_Dir = RIGHT;
    ps->_Status = OK;
}

void Pause()
{
    while (1)
    {
        Sleep(100);
        if (GetAsyncKeyState(VK_SPACE))//再按一下空格键,跳出暂停函数,继续移动
        {
            break;
        }
    }
}

int NextIsFood(pSnake ps, pSnakeNode pn)
{
    if ((ps->_pFood->x == pn->x) && (ps->_pFood->y == pn->y))
    {
        return 1;
    }
    return 0;
}

void EatFood(pSnake ps, pSnakeNode pn)
{
    pn->next = ps->_pSnake;
    ps->_pSnake = pn;
    //打印新增加的结点
    SetPos(ps->_pSnake->x, ps->_pSnake->y);
    printf(FOOD);
    ps->_TotalScore += ps->_AddScore;
    //食物被吃掉了,要再重新生成一个
    CreateFood(ps);
}

void NoFood(pSnake ps, pSnakeNode pn)
{
    pSnakeNode cur = NULL;
    pn->next = ps->_pSnake;
    ps->_pSnake = pn;
    //打印新增加的结点
    SetPos(ps->_pSnake->x, ps->_pSnake->y);
    printf(FOOD);
    //处理尾结点
    cur = ps->_pSnake;
    while (cur->next->next)
    {
        cur = cur->next;
    }
    SetPos(cur->next->x, cur->next->y);
    printf(" ");
    free(cur->next);
    cur->next = NULL;
}

void PrintHelpIafo(pSnake ps)
{
    SetPos(62, 11);
    printf("当前总分为:%d", ps->_TotalScore);
    SetPos(62, 12);
    printf("当前得分为:%d", ps->_AddScore);
    SetPos(62, 13);
    printf("温馨提示:用↑,↓,←,→");
    SetPos(62, 14);
    printf("控制蛇的运动方向");
    SetPos(62, 15);
    printf("加速得分更多哦");
    SetPos(62, 16);
    printf("按空格暂停游戏");
    SetPos(62, 17);
    printf("按ESC退出游戏");
}

void SnakeMove(pSnake ps)
{
    PrintHelpIafo(ps);
    pSnakeNode pNext = BuyNode();
    switch (ps->_Dir)
    {
    case UP:
    {
               pNext->x = ps->_pSnake->x;
               pNext->y = ps->_pSnake->y - 1;
               //是食物
               if (NextIsFood(ps, pNext))
               {
                   EatFood(ps, pNext);
               }
               else   //不是食物
                   NoFood(ps, pNext);
               break;
    }
    case DOWN:
    {
                 pNext->x = ps->_pSnake->x;
                 pNext->y = ps->_pSnake->y + 1;
                 //是食物
                 if (NextIsFood(ps, pNext))
                 {
                     EatFood(ps, pNext);
                 }
                 else   //不是食物
                     NoFood(ps, pNext);
                 break;
    }
    case LEFT:
    {
                 pNext->x = ps->_pSnake->x - 2;
                 pNext->y = ps->_pSnake->y;
                 //是食物
                 if (NextIsFood(ps, pNext))
                 {
                     EatFood(ps, pNext);
                 }
                 else   //不是食物
                     NoFood(ps, pNext);
                 break;
    }
    case RIGHT:
    {
                  pNext->x = ps->_pSnake->x + 2;
                  pNext->y = ps->_pSnake->y;
                  //是食物
                  if (NextIsFood(ps, pNext))
                  {
                      EatFood(ps, pNext);
                  }
                  else   //不是食物
                      NoFood(ps, pNext);
                  break;
    }
    }
}

void KillByWall(pSnake ps)
{
    if ((ps->_pSnake->x == 0) || (ps->_pSnake->x == 58) || (ps->_pSnake->y == 0) || (ps->_pSnake->y == 27))
        ps->_Status = KILL_BY_WALL;
}

void KillBySelf(pSnake ps)
{
    pSnakeNode pNext = ps->_pSnake->next;
    while (pNext)
    {
        if ((ps->_pSnake->x == pNext->x) && (ps->_pSnake ->y == pNext->y))
        {
            ps->_Status = KILL_BY_SELF;
        }
        pNext = pNext->next;
    }
}

void GameRun(pSnake ps)
{
    //确定方向
    do
    {
        if (GetAsyncKeyState(VK_UP) && ps->_Dir != DOWN)
        {
            ps->_Dir = UP;
        }
        else if (GetAsyncKeyState(VK_DOWN) && ps->_Dir != UP)
        {
            ps->_Dir = DOWN;
        }
        else if (GetAsyncKeyState(VK_LEFT) && ps->_Dir != RIGHT)
        {
            ps->_Dir = LEFT;
        }
        else if (GetAsyncKeyState(VK_RIGHT) && ps->_Dir != LEFT)
        {
            ps->_Dir = RIGHT;
        }
        else if (GetAsyncKeyState(VK_SPACE))//空格键暂停游戏
        {
            Pause();
        }
        else if (GetAsyncKeyState(VK_ESCAPE))//ESC退出游戏
        {
            ps->_Status = NORMAL_END;
            break;
        }
        else if (GetAsyncKeyState(VK_F1))//F1加速
        {
            if (ps->_SleepTime >= 40)//应该有一个极限,如果ps->SleepTime=0,就不休眠,太快了,没办法控制
            {
                ps->_SleepTime -= 20;//每次休眠时间减少20毫秒(速度加快)
                ps->_AddScore += 2;//越快得分越高,每次在原来的基础上加2
            }
        }
        else if (GetAsyncKeyState(VK_F2))//F2减速
        {
            if (ps->_SleepTime <= 300)//_AddScore的初始值是10,没慢一下减2,ps->SleepTime(初始值为100)加20,若_SleepScore>200,_AddScore的值就变为负值了,不合理
            {
                ps->_SleepTime += 20;
                ps->_AddScore -= 2;
            }
            if (ps->_SleepTime > 300)
            {
                ps->_AddScore = 1;
            }
        }
        //蛇的移动
        SnakeMove(ps);

        Sleep(ps->_SleepTime);//在蛇每移动一下要暂停(移动的慢一点,让肉眼可以看清它的移动,也来的急控制下一步的方向)一下,不然会一直停不下来

        //被墙撞死
        KillByWall(ps);

        //被自己撞死
        KillBySelf(ps);

    } while (ps->_Status == OK);

}

void GameEnd(pSnake ps)
{
    SetPos(26, 14);
    if (ps->_Status == NORMAL_END)
    {
        printf("正常退出");
    }
    if (ps->_Status == KILL_BY_WALL)
    {
        printf("哎呀,撞墙了");
    }
    if (ps->_Status == KILL_BY_SELF)
    {
        printf("不好意思,你自杀了");
    }
    SetPos(80, 27);
    pSnakeNode cur = ps->_pSnake;
    while (cur)
    {
        pSnakeNode Del = NULL;
        Del = cur;
        cur = cur->next;
        free(Del);
        Del = NULL;
    }
    ps->_pSnake = NULL;
    free(ps->_pFood);
    ps->_pFood = NULL;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"Snake.h"

void test()
{
    Snake snake = { 0 };//创建贪吃蛇

    srand((unsigned int)time(NULL));
    //游戏开始
    GameStart(&snake);

    //游戏开始跑
    GameRun(&snake);

    //游戏结束
    GameEnd(&snake);
}

int main()
{
    test();
    system("pause");
    return 0;
}

下来我们来看看测试结果如何:
这里写图片描述
这里写图片描述

猜你喜欢

转载自blog.csdn.net/huaijiu123/article/details/82054208