0
点赞
收藏
分享

微信扫一扫

C--贪吃蛇

穿裙子的程序员 2024-05-16 阅读 26

目录

前言

简单的准备工作

蛇的节点

开始前 void GameStart(pSnake ps)

void WelcomeToGame()

void CreateMap()

  void InitSnake(pSnake ps)

 void CreateFood(pSnake ps)

游戏进行 void GameRun(pSnake ps)

  int NextIsFood(pSnakeNode psn, pSnake ps)

 void NoFood(pSnakeNode psn, pSnake ps)

void SnakeMove(pSnake ps)

游戏结束 void GameEnd(pSnake ps)

源码

test.c

snake.h

snake.cpp

总结


前言

简单的准备工作

效果展示

屏幕录制 2024-05-16 092902

蛇的节点

在游戏运⾏的过程中,蛇每次吃⼀个⻝物,蛇的⾝体就会变⻓⼀节,如果我们使⽤链表存储蛇的信 息,那么蛇的每⼀节其实就是链表的每个节点。每个节点只要记录好蛇⾝节点在地图上的坐标就⾏, 所以蛇节点结构如下:

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

但是这样不好维护这一条蛇,要管理整条贪吃蛇,我们再封装⼀个Snake的结构来维护整条贪吃蛇

typedef struct Snake
 {
 pSnakeNode _pSnake;维护整条蛇的指针 
pSnakeNode _pFood;维护⻝物的指针 
enum DIRECTION _Dir;头的⽅向默认是向右 
enum GAME_STATUS _Status;游戏状态 
int _Socre;当前获得分数 
int _Add;默认每个⻝物10分 
int _SleepTime;每⾛⼀步休眠时间
}Snake, * pSnake;

开始前 void GameStart(pSnake ps)

游戏开始,先打印蛇的身体和地图,以及一些帮助的提示语句,先完成一些准备工作

void GameStart(pSnake ps)
 {
 
设置控制台窗⼝的⼤⼩,30⾏,100列 
//mode 为DOS命令
 system("mode con cols=100 lines=30");
 //设置cmd窗⼝名称
 system("title 贪吃蛇"); 
//获取标准输出的句柄(⽤来标识不同设备的数值) 
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE); //影藏光标操作 
CONSOLE_CURSOR_INFO CursorInfo; //打印欢迎界⾯ 
WelcomeToGame(); //打印地图 
CreateMap(); //初始化蛇 
GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息 
CursorInfo.bVisible = false; //隐藏控制台光标 
SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态 
InitSnake(ps);
 //创造第⼀个⻝物 
CreateFood(ps);
 }
 

void WelcomeToGame()

在游戏正式开始之前,做⼀些功能提醒,来让玩家了解游戏方法与规则,其中SetPos是一个已经封装后的函数,可以把这个字体打在这个坐标

 void WelcomeToGame()
 {
 SetPos(40, 15);
 printf("欢迎来到贪吃蛇⼩游戏");
 SetPos(40, 25);//让按任意键继续的出现的位置好看点 
system("pause");
 system("cls");
 SetPos(25, 12);
 printf("⽤ ↑ . ↓ . ← . → 分别控制蛇的移动, F3为加速,F4为减速\n");
 SetPos(25, 13);
 printf("加速将能得到更⾼的分数。\n");
 SetPos(40, 25);//让按任意键继续的出现的位置好看点 
system("pause");
 system("cls");
 }

void CreateMap()

 void CreateMap()
 {
 int i = 0;
 //上(0,0)-(56, 0) 
SetPos(0, 0);
 for (i = 0; i < 58; i += 2)
 {
 wprintf(L"%c", WALL);
 } //下(0,26)-(56, 26)  
 SetPos(0, 26); for (i = 0; i < 58; i += 2)
 { wprintf(L"%c", WALL);
 } //左//x是0,y从1开始增⻓ 
for (i = 1; i < 26; i++)
 {
 SetPos(0, i);
 wprintf(L"%c", WALL);}  //是56y从1开始增⻓ 
for (i = 1; i < 26; i++)
 {
 SetPos(56, i);
 wprintf(L"%c", WALL);
 }
 } 

  void InitSnake(pSnake ps)

void InitSnake(pSnake ps)
 {
    pSnakeNode cur = NULL;
    int i = 0;
    //创建蛇⾝节点,并初始化坐标 
    //头插法
    for (i = 0; i < 5; i++)
    {
        //创建蛇⾝的节点 
        cur = (pSnakeNode)malloc(sizeof(SnakeNode));
        if (cur == NULL)
        {
            perror("InitSnake()::malloc()");
            return;
        }
        //设置坐标 
        cur->next = NULL;
        cur->x = POS_X + i * 2;
        cur->y = POS_Y;
        //头插法 
        if (ps->_pSnake == NULL)
        {
            ps->_pSnake = cur;
        }
        else
        {
            cur->next = ps->_pSnake;
            ps->_pSnake = cur;
        }
    }
    //打印蛇的⾝体
    cur = ps->_pSnake;
    while (cur)
    {
 SetPos(cur->x, cur->y);
 wprintf(L"%c", BODY);
 cur = cur->next;
 }
 //
初始化贪吃蛇数据 
ps->_SleepTime = 200;
 ps->_Socre = 0;
 ps->_Status = OK;
 ps->_Dir = RIGHT;
 ps->_Add = 10;
 }

 void CreateFood(pSnake ps)

 void CreateFood(pSnake ps)
 {
    int x = 0;
    int y = 0;
 again:
    //产⽣的x坐标应该是2倍数,这样才可能和蛇头坐标对⻬。 
    do
    {
        x = rand() % 53 + 2;
        y = rand() % 25 + 1;
    } while (x % 2 != 0);
    pSnakeNode cur = ps->_pSnake;//获取指向蛇头的指针
     //⻝物不能和蛇⾝冲突
    while (cur)
    {
        if (cur->x == x && cur->y == y)
        {
            goto again;
        }
        cur = cur->next;
    }
    pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode)); //创建⻝物
    if (pFood == NULL)
    {
        perror("CreateFood::malloc()");
        return;
    }
    else
    {
        pFood->x = x;
        pFood->y = y;
        SetPos(pFood->x, pFood->y);
        wprintf(L"%c", FOOD);
        ps->_pFood = pFood;
    }
 }

游戏进行 void GameRun(pSnake ps)

  int NextIsFood(pSnakeNode psn, pSnake ps)

是一个判断下一个位置是食物的函数,会在下面SnakeMove起到作用,是食物的话将吃掉,不是的话将继续按按键运动

 
int NextIsFood(pSnakeNode psn, pSnake ps)
 {
    return (psn->x == ps->_pFood->x) && (psn->y == ps->_pFood->y);
 }
 

 void NoFood(pSnakeNode psn, pSnake ps)

将下⼀个节点头插⼊蛇的⾝体,并将之前蛇⾝最后⼀个节点打印为空格,放弃掉蛇⾝的最后⼀个节点,在时间是毫米的瞬间完成,造成移动的效果

void NoFood(pSnakeNode psn, pSnake ps)
 {
 //头插法 
psn->next = ps->_pSnake;
 ps->_pSnake = psn;
 pSnakeNode cur = ps->_pSnake;
 //打印蛇 
while (cur->next->next)
 {
 SetPos(cur->x, cur->y);
 wprintf(L"%c", BODY);
 }
 //最后⼀个位置打印空格,然后释放节点 
SetPos(cur->next->x, cur->next->y);
 printf("  ");
 free(cur->next);
 cur->next = NULL;
 }

void SnakeMove(pSnake ps)

 void SnakeMove(pSnake ps)
 {
 //创建下⼀个节点 
pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));
 if (pNextNode == NULL)
 {
 perror("SnakeMove()::malloc()");
 return;
 } //确定下⼀个节点的坐标,下⼀个节点的坐标根据,蛇头的坐标和⽅向确定 
switch (ps->_Dir)
 {
 case UP:
 {
 pNextNode->x = ps->_pSnake->x;
 pNextNode->y = ps->_pSnake->y - 1;

        }
        break;
        case DOWN:
        {
            pNextNode->x = ps->_pSnake->x;
            pNextNode->y = ps->_pSnake->y + 1;
        }
        break;
        case LEFT:
        {
            pNextNode->x = ps->_pSnake->x - 2;
            pNextNode->y = ps->_pSnake->y;
        }
        break;
        case RIGHT:
        {
            pNextNode->x = ps->_pSnake->x + 2;
            pNextNode->y = ps->_pSnake->y;
        }
        break;
    }
    //如果下⼀个位置就是⻝物
 
    if (NextIsFood(pNextNode, ps))
    {
        EatFood(pNextNode, ps);
    }
    else//如果没有⻝物 
    {
        NoFood(pNextNode, ps);
    }
    KillByWall(ps);
    KillBySelf(ps);
 }

游戏结束 void GameEnd(pSnake ps)

游戏结束 游戏状态不再是OK(游戏继续)的时候,要告知游戏技术的原因,并且释放蛇⾝节点。

 void GameEnd(pSnake ps)
 {
 pSnakeNode cur = ps->_pSnake;
 SetPos(24, 12);
 switch (ps->_Status)
 {
 case END_NOMAL:
 printf("您主动退出游戏\n");
 break;
 case KILL_BY_SELF: printf("您撞上⾃⼰了 ,游戏结束!\n");
 break;
 case KILL_BY_WALL:
printf("您撞墙了,游戏结束!\n"); } //释放蛇⾝的节点 
while (cur)
 {
 pSnakeNode del = cur;
 cur = cur->next;
 free(del);
 }
 }

源码

test.c

 #include "Snake.h"
 #include <locale.h>
 void test()
 {
 int ch = 0;
 srand((unsigned int)time(NULL));
 do
 {
 Snake snake = { 0 };
 GameStart(&snake);
 GameRun(&snake);
 GameEnd(&snake);
 SetPos(20, 15);
 printf("再来⼀局吗?(Y/N):");
 ch = getchar();
 getchar();//清理\n } while (ch == 'Y');
 SetPos(0, 27);
 }
 int main()
 {
 //修改当前地区为本地模式,为了⽀持中⽂宽字符的打印
 setlocale(LC_ALL, ""); 
test();
 return 0;
 }

snake.h

 #pragma once
 #include <windows.h>
 #include <time.h>
 #include <stdio.h>
 #define KEY_PRESS(VK)  ((GetAsyncKeyState(VK)&0x1) ? 1 : 0)
 //⽅向
enum DIRECTION
 {
 UP = 1,
 DOWN,
 LEFT,
 RIGHT
 };
 //游戏状态 
enum GAME_STATUS
 {
 OK,//正常运⾏ 
KILL_BY_WALL,//撞墙 
KILL_BY_SELF,//咬到⾃⼰ 
END_NOMAL//正常结束 
};
 #define WALL L'□' 
#define BODY L'●'  // ★ ○● ◇◆ □■ 
#define FOOD L' ★ '  // ★ ○● ◇◆ □■ //蛇的初始位置 
#define POS_X 24
 #define POS_Y 5 //蛇⾝节点
 typedef struct SnakeNode
 {
 int x;
 int y;
 struct SnakeNode* next;
 }SnakeNode, * pSnakeNode;
 typedef struct Snake
 {
 pSnakeNode _pSnake;//整条蛇的指针 
pSnakeNode _pFood;//维护⻝物的指针 
enum DIRECTION _Dir;//蛇头的⽅向默认是向右 
enum GAME_STATUS _Status;//游戏状态 
int _Socre;//当前获得分数 
int _Add;//默认每个⻝物 
int _SleepTime;//每⾛⼀步休眠时间 
}Snake, * pSnake;
 //游戏开始前的初始化 
void GameStart(pSnake ps); //游戏运⾏过程 
void GameRun(pSnake ps); //结束 
void GameEnd(pSnake ps); //设置光标的坐标 
void SetPos(short x, short y); //欢迎界⾯ 
void WelcomeToGame(); //打印帮助信息 
void PrintHelpInfo(); //创建地图 
void CreateMap();//初始化蛇 
void InitSnake(pSnake ps); //⻝物 
void CreateFood(pSnake ps);暂停响应 
void pause(); //下⼀个节点是⻝物
 int NextIsFood(pSnakeNode psn, pSnake ps); //吃⻝物 
void EatFood(pSnakeNode psn, pSnake ps); //不吃⻝物 
void NoFood(pSnakeNode psn, pSnake ps); //撞墙检测 
int KillByWall(pSnake ps); //撞⾃⾝检测 
int KillBySelf(pSnake ps); //蛇的移动 
void SnakeMove(pSnake ps); //游戏初始化 
void GameStart(pSnake ps); //游戏运⾏ 
void GameRun(pSnake ps); //游戏结束
 void GameEnd(pSnake ps);

snake.cpp

 #include "Snake.h"
 //设置光标的坐标 
void SetPos(short x, short y)
 {
 COORD pos = { x, y };
 HANDLE hOutput = NULL;//获取标准输出的句柄(⽤来标识不同设备的数值) 
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//设置标准输出上光标的位置为
pos SetConsoleCursorPosition(hOutput, pos);
 }
 void WelcomeToGame()
 {
 SetPos(40, 15);
 printf("欢迎来到贪吃蛇⼩游戏");
 SetPos(40, 25);//让按任意键继续的出现的位置好看点 
system("pause");
 system("cls");
 SetPos(25, 12);
 printf("⽤ ↑ . ↓ . ← . → 控制蛇的移动, F3为加速,F4为减速\n");
 SetPos(25, 13);
 printf("加速将能得到更⾼的分数。\n");
 SetPos(40, 25);//让按任意键继续的出现的位置好看点 
system("pause");
 system("cls"); } void CreateMap()
 {
 int i = 0;
 //上(0,0)-(56, 0) 
SetPos(0, 0);
 for (i = 0; i < 58; i += 2)
 {
 wprintf(L"%c", WALL);
 }
 //
下(0,26)-(56, 26) 
SetPos(0, 26);
 for (i = 0; i < 58; i += 2)
 {
 wprintf(L"%c", WALL);
 }
 //左 
//x0,y从1开始增⻓ 
for (i = 1; i < 26; i++)
 {
 SetPos(0, i);
 wprintf(L"%c", WALL);
 }
 //56,从开始增⻓ 
for (i = 1; i < 26; i++)
 {
 SetPos(56, i);
 wprintf(L"%c", WALL);
 }
 }
void InitSnake(pSnake ps)
 {
    pSnakeNode cur = NULL;
    int i = 0;
    //创建蛇⾝节点,并初始化坐标
     //头插法
 
    for (i = 0; i < 5; i++)
    {
        //创建蛇⾝的节点
 cur = (pSnakeNode)malloc(sizeof(SnakeNode));
        if (cur == NULL)
        {
            perror("InitSnake()::malloc()");
            return;
        }
        //设置坐标
        cur->next = NULL;
        cur->x = POS_X + i * 2;
        cur->y = POS_Y;
        //头插法
        if (ps->_pSnake == NULL)
        {
            ps->_pSnake = cur;
        }
        else
        {
            cur->next = ps->_pSnake;
            ps->_pSnake = cur;
        }
    }
    //打印蛇的⾝体 
    cur = ps->_pSnake;
    while (cur)
    {
        SetPos(cur->x, cur->y);
        wprintf(L"%c", BODY);
        cur = cur->next;
    }
    //初始化贪吃蛇数据
    ps->_SleepTime = 200;
    ps->_Socre = 0;
    ps->_Status = OK;
    ps->_Dir = RIGHT;
    ps->_Add = 10;
 }
 void CreateFood(pSnake ps)
 {
    int x = 0;
    int y = 0;
 again:
    //产⽣的x坐标应该是2的倍数,这样才可能和蛇头坐标对⻬。
    do
    {
        x = rand() % 53 + 2;
        y = rand() % 25 + 1;
    } while (x % 2 != 0);
    pSnakeNode cur = ps->_pSnake;//指向蛇头的指针     //⻝物不能和蛇⾝冲突
    while (cur)
    {
        if (cur->x == x && cur->y == y)
        {
            goto again;
        }
        cur = cur->next;
    }
    pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode)); //
创建⻝物
 
    if (pFood == NULL)
    {
        perror("CreateFood::malloc()");
        return;
    }
    else
    {
        pFood->x = x;
        pFood->y = y;
        SetPos(pFood->x, pFood->y);
        wprintf(L"%c", FOOD);
        ps->_pFood = pFood;
    }
 }
 void PrintHelpInfo()
 {
 }
 void pause()//暂停 
{打印提⽰信息 
SetPos(64, 15);
 printf("不能穿墙,不能咬到⾃⼰\n");
 SetPos(64, 16);
 printf("⽤↑.↓.←.→分别控制蛇的移动.");
 SetPos(64, 17);
 printf("F1 为加速,F2 为减速\n");
 SetPos(64, 18);
 printf("ESC :退出游戏.space:暂停游戏.");
 SetPos(64, 20);
 while (1)
 {
 Sleep(300);
 if (KEY_PRESS(VK_SPACE))
 {
 break;
 }
 }
 }
 //pSnakeNode psn 是下⼀个节点的地址 
//pSnake ps 维护蛇的指针 
int NextIsFood(pSnakeNode psn, pSnake ps)
 {
 return (psn->x == ps->_pFood->x) && (psn->y == ps->_pFood->y);
 }
 //pSnakeNode psn 是下⼀个节点的地址 
//pSnake ps 维护蛇的指针 
void EatFood(pSnakeNode psn, pSnake ps)
 {
 //头插法 
psn->next = ps->_pSnake;
 ps->_pSnake = psn;
 pSnakeNode cur = ps->_pSnake;
 //打印蛇 
while (cur)
 {
 SetPos(cur->x, cur->y);
 wprintf(L"%c", BODY);
 cur = cur->next;

 }
 ps->_Socre += ps->_Add;
 free(ps->_pFood);
 CreateFood(ps);
 }
 //pSnakeNode psn 
是下⼀个节点的地址
 
//pSnake ps 
维护蛇的指针
 
void NoFood(pSnakeNode psn, pSnake ps)
 {
 //头插法
 psn->next = ps->_pSnake;
 ps->_pSnake = psn;
 pSnakeNode cur = ps->_pSnake; //打印蛇 
while (cur->next->next)
 {
 SetPos(cur->x, cur->y);
 wprintf(L"%c", BODY);
 cur = cur->next;
 }
 //最后⼀个位置打印空格,然后释放节点 
SetPos(cur->next->x, cur->next->y);
 printf("  ");
 free(cur->next);
 cur->next = NULL;
 }
 //pSnake ps 
维护蛇的指针
int KillByWall(pSnake ps)
 {
 if ((ps->_pSnake->x == 0)
 || (ps->_pSnake->x == 56)
 || (ps->_pSnake->y == 0)
 || (ps->_pSnake->y == 26))
 {
 ps->_Status = KILL_BY_WALL;
 return 1;
 }
 return 0;
 }
 //pSnake ps 
维护蛇的指针 
int KillBySelf(pSnake ps)
 {
    pSnakeNode cur = ps->_pSnake->next;
    while (cur)
    {
        if ((ps->_pSnake->x == cur->x)
            && (ps->_pSnake->y == cur->y))
        {
            ps->_Status = KILL_BY_SELF;
            return 1;
        }
        cur = cur->next;
    }
    return 0;
 }
 void SnakeMove(pSnake ps)
 {
    //创建下⼀个节点 
    pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));
    if (pNextNode == NULL)
    {
        perror("SnakeMove()::malloc()");
        return;
    }
    //
确定下⼀个节点的坐标,下⼀个节点的坐标根据,蛇头的坐标和⽅向确定
 
    switch (ps->_Dir)
    {
        case UP:
        {
            pNextNode->x = ps->_pSnake->x;
            pNextNode->y = ps->_pSnake->y - 1;
        }
        break;
        case DOWN:
        {
            pNextNode->x = ps->_pSnake->x;
            pNextNode->y = ps->_pSnake->y + 1;
        }
        break;
        case LEFT:
        {
            pNextNode->x = ps->_pSnake->x - 2;
            pNextNode->y = ps->_pSnake->y;
        }
        break;
        case RIGHT:
        {
            pNextNode->x = ps->_pSnake->x + 2;
            pNextNode->y = ps->_pSnake->y;
        }
        break;
    }
    //如果下⼀个位置就是⻝物
 
    if (NextIsFood(pNextNode, ps))
    {
        EatFood(pNextNode, ps);
    }
    else//如果没有⻝物

    {
        NoFood(pNextNode, ps);
    }
    KillByWall(ps);
    KillBySelf(ps);
 }
 void GameStart(pSnake ps)
 {
    //设置控制台窗⼝的⼤⼩,30⾏,100 
    //mode 为DOS命令 
    system("mode con cols=100 lines=30");
    //设置cmd窗⼝名称 
    system("title 贪吃蛇"); 
    //获取标准输出的句柄(来标识不同设备的数值) 
    HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);  //影藏光标操作
    CONSOLE_CURSOR_INFO CursorInfo;
    GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息
    CursorInfo.bVisible = false; //隐藏控制台光标 
    SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态 //打印欢迎界⾯ 
    WelcomeToGame();   //打印地图 
   CreateMap();   //初始化蛇
    InitSnake(ps);  /创造第⼀个⻝物
    CreateFood(ps);
 
}
 void GameRun(pSnake ps)
 {    //打印右侧帮助信息
 
    PrintHelpInfo();
    do
    {
        SetPos(64, 10);
        printf("得分:%d ", ps->_Socre);        printf("每个⻝物得分:%d分", ps->_Add);
        if (KEY_PRESS(VK_UP) && ps->_Dir != DOWN)
        {
            ps->_Dir = UP;
        }
        else if (KEY_PRESS(VK_DOWN) && ps->_Dir != UP)
        {
            ps->_Dir = DOWN;
        }
        else if (KEY_PRESS(VK_LEFT) && ps->_Dir != RIGHT)
        {
            ps->_Dir = LEFT;
        }
        else if (KEY_PRESS(VK_RIGHT) && ps->_Dir != LEFT)
        {
            ps->_Dir = RIGHT;
        }
        else if (KEY_PRESS(VK_SPACE))
        {
            pause();
        }
        else if (KEY_PRESS(VK_ESCAPE))
        {
            ps->_Status = END_NOMAL;
            break;
        }
        else if (KEY_PRESS(VK_F3))
        {
            if (ps->_SleepTime >= 50)
            {
                ps->_SleepTime -= 30;
                ps->_Add += 2;
            }
        }
        else if (KEY_PRESS(VK_F4))
        {

            if (ps->_SleepTime < 350)
            {
                ps->_SleepTime += 30;
                ps->_Add -= 2;
                if (ps->_SleepTime == 350)
                {
                    ps->_Add = 1;
                }
            }
        }
        //蛇每次⼀定之间要休眠的时间,时间短,蛇移动速度就快
 
        Sleep(ps->_SleepTime);
        SnakeMove(ps);
    } while (ps->_Status == OK);
 }
 void GameEnd(pSnake ps)
 {
    pSnakeNode cur = ps->_pSnake;
    SetPos(24, 12);
    switch (ps->_Status)
    {
    case END_NOMAL:        printf("您主动退出游戏\n");
        break;
    case KILL_BY_SELF:
        printf("您撞上⾃⼰了 ,游戏结束!\n");        break;
    case KILL_BY_WALL:
        printf("您撞墙了,游戏结束!\n");
        break;
    }
    //释放蛇⾝的节点 
    while (cur)
    {
        pSnakeNode del = cur;
        cur = cur->next;
        free(del);
    }
 }

总结

举报

相关推荐

0 条评论