C语言实现双人贪吃蛇项目(基于控制台界面)

一.贪吃蛇

贪吃蛇是一款简单而富有乐趣的游戏,它的规则易于理解,但挑战性也很高。它已经成为经典的游戏之一,并且在不同的平台上一直受到人们的喜爱和回忆。

二.贪吃蛇的功能

  1. 游戏控制:玩家可以使用键盘输入设备来控制蛇的移动方向。

  2. 蛇的移动:蛇头会根据玩家的输入方向进行移动,而蛇身会随着蛇头的移动而延长,形成一条越来越长的蛇。

  3. 食物生成:游戏界面会随机生成食物,玩家控制的蛇头需要吃掉这些食物,每吃到一块食物,蛇的身体就会增长一节。

  4. 碰撞检测:游戏会检测蛇头是否与自身的身体或者墙壁碰撞,如果碰撞则游戏结束。

  5. 分数计算:游戏会记录玩家吃到的食物数量,并根据数量进行得分计算,通常吃到的食物越多,得分越高。

  6. 难度递增:随着蛇身越来越长,游戏的难度也会逐渐增加,因为玩家需要更小心地避免碰撞

  7. 双人游戏:贪吃蛇支持双人游玩,两位玩家可以相互竞争。

三.贪吃蛇项目的实现

1.游戏前的准备

1.1游戏的状态

  • enum state 枚举类型定义了游戏的状态,包括以下常量:
    • ok:正常状态,表示游戏进行中。
    • by_wall:撞墙状态,表示蛇撞到了地图的边界。
    • by_body:撞到蛇的身体状态,表示蛇头撞到了蛇身。
    • by_self:蛇咬到自己状态,表示蛇头咬到了自己的身体。
    • by_end:游戏结束状态。
    • by_headpush:蛇头相互碰撞状态,表示两个玩家的蛇头相互碰撞。
enum state
{
    ok = 1,         // 游戏状态:正常状态
    by_wall,        // 游戏状态:撞墙
    by_body,        // 游戏状态:撞到蛇的身体
    by_self,        // 游戏状态:蛇咬到自己
    by_end,         // 游戏状态:游戏结束
    by_headpush     // 游戏状态:蛇头相互碰撞
};

1.2蛇的移动方向

  • enum direction 枚举类型定义了蛇的移动方向,包括以下常量:
    • up:表示向上移动。
    • down:表示向下移动。
    • left:表示向左移动。
    • right:表示向右移动。
enum direction
{
    up = 1,         // 方向:上
    down,           // 方向:下
    left,           // 方向:左
    right           // 方向:右
};

1.3蛇的节点

  • struct SnakeNode 结构体用于表示蛇的一个节点,包括以下成员:
    • x:节点的横坐标。
    • y:节点的纵坐标。
    • next:指向下一个节点的指针。
typedef struct SnakeNode
{
    int x;                  // 蛇节点的横坐标
    int y;                  // 蛇节点的纵坐标
    struct SnakeNode* next; // 指向下一个蛇节点的指针
} SnakeNode, * pSnakeNode;

1.4食物的位置

  • struct Food 结构体用于表示食物的位置,包括以下成员:
    • x:食物的横坐标。
    • y:食物的纵坐标。
typedef struct Food
{
    int x;                  // 食物的横坐标
    int y;                  // 食物的纵坐标
} Food, * pFood;

1.5整个贪吃蛇游戏

  • struct Snake 结构体用于表示蛇,包括以下成员:
    • _snake:蛇的头节点指针。
    • _food:食物指针。
    • _score:蛇的得分。
    • dir:蛇的移动方向。
    • sta:当前游戏状态。
    • _weight:奖励。
    • _sleeptime:游戏循环每次暂停的时间间隔。
typedef struct Snake
{
    pSnakeNode _snake;      // 蛇的头节点指针
    pFood _food;            // 食物指针
    int _score;             // 蛇的得分
    enum direction dir;     // 蛇的移动方向
    enum state sta;         // 当前游戏状态
    int _weight;            // 奖励
    int _sleeptime;         // 游戏循环每次暂停的时间间隔
} Snake, * pSnake;

2.游戏开始

2.1本地化设置

使用setlocale函数设置本地化环境为当前系统默认的环境。因为环境的差异,导致我们的中文的宽字符无法被识别,所以要本地化设置。

int main()
{
    setlocale(LC_ALL, "");  // 设置本地化环境为当前系统默认的环境

    test();  // 调用test函数进行测试

    return 0;  // 返回0表示程序正常结束
}

2.2 实现贪吃蛇的基本流程

test函数中,通过srand函数将当前时间作为随机数种子。然后使用do-while循环进行游戏的测试和循环控制。在循环内部,首先使用malloc动态分配了两个Snake结构体的内存,并将其指针赋值给snake1snake2。接着调用gamestart函数开始游戏,传入snake1snake2作为参数;然后调用gamerun函数进行游戏运行,同样传入snake1snake2作为参数;最后调用gameend函数结束游戏并释放内存,同样传入snake1snake2作为参数。之后,使用system("cls")清空控制台屏幕,然后在指定位置打印提示信息。接下来,使用getchar函数获取一个字符并赋值给变量ch再使用getchar读取多余的换行符。最后,判断ch是否为字符'y''Y',如果是则继续进行下一轮游戏。

#include "snake.h"  // 引入自定义的头文件snake.h

void test()
{
    srand((unsigned int)time(NULL));  // 使用当前时间作为随机数种子

    int ch;
    do {
        pSnake snake1 = (pSnake)malloc(sizeof(Snake));  // 动态分配一个Snake结构体的内存,并将其指针赋给snake1
        pSnake snake2 = (pSnake)malloc(sizeof(Snake));  // 动态分配一个Snake结构体的内存,并将其指针赋给snake2

        gamestart(snake1, snake2);  // 调用gamestart函数开始游戏,传入snake1和snake2作为参数
        gamerun(snake1, snake2);    // 调用gamerun函数进行游戏运行,传入snake1和snake2作为参数
        gameend(snake1, snake2);    // 调用gameend函数结束游戏,释放内存,传入snake1和snake2作为参数

        system("cls");  // 清空控制台屏幕

        setpos(46, 15);  // 设置光标位置为(46, 15)
        printf("选择Y/N");

        setpos(50, 16);  // 设置光标位置为(50, 16)
        ch = getchar();  // 从标准输入中获取一个字符,赋值给ch
        getchar();       // 读取多余的换行符

    } while (ch == 'y' || ch == 'Y');  // 如果输入的字符是'y'或'Y',则继续进行下一轮游戏

}

2.3设置光标

该函数接受两个整型参数xy,分别表示光标的横坐标和纵坐标。在函数内部,首先调用GetStdHandle函数获取标准输出句柄,该句柄用于操作控制台窗口。然后创建一个COORD结构体变量coord,并将x赋值给coord.X,将y赋值给coord.Y,即设置光标的位置。最后,调用SetConsoleCursorPosition函数将控制台光标位置设置为指定的坐标。这样,在调用setpos函数时,控制台光标会移动到指定的位置。

void setpos(int x, int y)
{
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);  // 获取标准输出句柄
    COORD coord;
    coord.X = x;  // 设置光标的横坐标为x
    coord.Y = y;  // 设置光标的纵坐标为y
    SetConsoleCursorPosition(handle, coord);  // 设置控制台光标位置为指定的坐标
}

 2.4游戏的初始化

该函数接受两个参数,snake1snake2,分别表示两条蛇的指针。函数开始使用断言assert,确保snake1snake2不为NULL,以保证后续操作的正确性。然后依次调用以下函数:

  • welcome:显示游戏欢迎信息,可能是在控制台输出一些欢迎文本。
  • creatmap:创建游戏地图,可能是在控制台上绘制一个游戏地图的界面。
  • initsnake:初始化蛇的身体,可能是将蛇的初始位置和长度设置为默认值。
  • creatsnake:生成两条蛇的初始位置,可能是将两条蛇放置在地图的指定位置。
  • creatfood:生成食物的位置,以供两条蛇争夺,可能是将食物随机放置在地图的空闲位置。
  • gameinfo:显示游戏信息,可能是在控制台上显示当前游戏的状态、得分等信息。
void gamestart(pSnake snake1, pSnake snake2)
{
    assert(snake1 && snake2);  // 使用断言确保snake1和snake2不为NULL

    welcome();                // 调用welcome函数,显示游戏欢迎信息
    creatmap();               // 调用creatmap函数,创建游戏地图
    initsnake(snake1);        // 调用initsnake函数,初始化snake1蛇身
    initsnake(snake2);        // 调用initsnake函数,初始化snake2蛇身
    creatsnake(snake1, snake2);// 调用creatsnake函数,生成snake1和snake2的初始位置
    creatfood(snake1, snake2);// 调用creatfood函数,生成食物的位置,以供snake1和snake2争夺
    creatfood(snake2, snake1);// 调用creatfood函数,生成食物的位置,以供snake2和snake1争夺
    gameinfo(snake1, snake2); // 调用gameinfo函数,显示游戏信息
}
2.4.1欢迎信息

首先调用system函数设置控制台窗口的大小为100列,30行,使用命令mode con cols=100 lines=30实现。然后调用system函数设置控制台窗口的标题为"贪吃蛇",使用命令title 贪吃蛇实现。接下来,获取标准输出句柄,并获取控制台光标信息。将光标的可见性设置为false,即不可见,然后再将修改后的光标信息设置回控制台。然后使用setpos函数设置光标位置为(40, 10),在该位置打印欢迎信息。接着,使用setpos函数设置光标位置为(40, 20),调用system("pause")暂停程序的执行,等待用户按下任意键。然后使用system("cls")清空控制台屏幕。接下来,使用setpos函数设置光标位置为(38, 10),然后依次打印游戏规则的内容。再次使用setpos函数设置光标位置为(40, 20),使用system("pause")暂停程序的执行,等待用户按下任意键。最后,使用system("cls")清空控制台屏幕。

void welcome()
{
    system("mode con cols=100 lines=30");  // 设置控制台窗口的大小为100列,30行
    system("title 贪吃蛇");                // 设置控制台窗口的标题为"贪吃蛇"

    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);  // 获取标准输出句柄
    CONSOLE_CURSOR_INFO cursor;
    GetConsoleCursorInfo(handle, &cursor);  // 获取控制台光标信息
    cursor.bVisible = false;                // 将光标设置为不
评论 43
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值