C语言 贪吃蛇小游戏的实现

引言

贪吃蛇是一款经典的小游戏,简单有趣,适合初学者用来练习编程技能。本文将详细解析一个用 C 语言实现的贪吃蛇游戏代码,代码主要包含三个文件:test.cpp、snake.h 和 snake.cpp。

代码结构概述

  • test.cpp:主要负责游戏的测试逻辑,包含 test 函数和 main 函数。
  • snake.h:头文件,包含必要的头文件引用、宏定义、类型声明和函数声明。
  • snake.cpp:实现了 snake.h 中声明的函数,包含游戏的初始化、运行、结束等功能。

代码详细解析

snake.h 文件

#pragma once

#include <locale.h>
#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>

#define POS_X 24
#define POS_Y 5
  • #pragma once 确保头文件只被包含一次,防止重复定义。
  • 引入了必要的头文件,用于输入输出、系统操作、随机数生成等。
  • 定义了宏 POS_X 和 POS_Y,用于指定蛇的初始位置。
    // 蛇的方向
    enum DIRECTION
    {
        UP,    // 上
        DOWN,  // 下
        LEFT,  // 左
        RIGHT  // 右
    };
    
    // 蛇的状态
    enum GAME_STATUS
    {
        OK,             // 正常
        KILL_BY_WALL,   // 撞墙
        KILL_BY_SELF,   // 撞到自己
        END_NORMAL      // 正常退出
    };

  • 定义了两个枚举类型 DIRECTION 和 GAME_STATUS,分别表示蛇的移动方向和游戏状态。
    // 蛇身的节点声明
    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 food_weight;       // 一个食物的分数
        int score;             // 总成绩
        int sleep_time;        // 休息时间,时间越短,蛇的速度越快
    } Snake, *pSnake;

  • 定义了蛇身节点结构体 SnakeNode 和贪吃蛇结构体 Snake,使用 typedef 简化类型名。
    //函数的声明
    
    //游戏的初始化
    void GameStart(pSnake ps);
    
    //获得坐标
    void SetPos(short x, short y);
    
    //欢迎界面
    void WelcomeToGame();
    
    //绘制地图
    void CreateMap();
    
    //初始化蛇
    void InitSnake(pSnake ps);
    
    //创造食物
    void CreateFood(pSnake ps);
    
    //运行游戏
    void GameRun(pSnake ps);
    
    //蛇的移动
    void SnakeMove(pSnake ps);
    
    //判断下一个节点处是否为食物
    int NextIsFood(pSnakeNode pn,pSnake ps);
    
    //下一个节点处是食物,吃掉食物
    void EatFood(pSnakeNode pn,pSnake ps);
    
    //下一个节点处不是食物
    void NoFood(pSnakeNode pn,pSnake ps);
    
    //检测蛇是否撞墙
    void KillByWall(pSnake ps);
    
    //检测蛇是否撞到自己
    void KillBySelf(pSnake ps);
    
    //游戏善后的工作
    void GameEnd(pSnake ps);

  • 声明了游戏中使用的所有函数,方便在 snake.cpp 中实现。
  • test.cpp 文件

    #include "snake.h"
    
    // 完成的是游戏的测试逻辑
    void test()
    {
        int ch = 0;
        do
        {
            system("cls");
            // 创建贪吃蛇
            Snake snake = { 0 };
            // 初始化游戏
            // 1. 设置窗口大小,标题以及光标隐藏
            // 2. 打印欢迎界面,功能介绍
            // 3. 绘制地图
            // 4. 创建蛇
            // 5. 创建食物
            GameStart(&snake);
            // 运行游戏
            GameRun(&snake);
            // 结束游戏 - 善后工作
            GameEnd(&snake);
            SetPos(20, 20);
            printf("再来一局吗(Y/N):");
            ch = getchar();
            while (getchar() != '\n');
        } while (ch == 'Y' || ch == 'y');
        SetPos(0, 27);
    }
    
    int main()
    {
        srand((unsigned int)time(NULL));
        test();
        return 0;
    }

  • test 函数是游戏的主循环,负责初始化游戏、运行游戏、结束游戏,并询问玩家是否再来一局。
  • main 函数初始化随机数种子,调用 test 函数开始游戏。

    snake.cpp 文件

    基本功能函数
    // 获得坐标
    void SetPos(short x, short y)
    {
        // 获得标准输出设备的句柄
        HANDLE houtput = NULL;
        houtput = GetStdHandle(STD_OUTPUT_HANDLE);
    
        // 定位光标的位置
        COORD pos = { x, y };
        SetConsoleCursorPosition(houtput, pos);

  • SetPos 函数用于设置控制台光标的位置,方便在指定位置输出字符。
    // 欢迎界面的打印
    void WelcomeToGame()
    {
        SetPos(40, 14);
        printf("欢迎来到贪吃蛇小游戏\n");
        SetPos(38, 20);
        system("pause");
        system("cls");
        SetPos(30, 14);
        printf("用 ↑, ↓, ←, → 来控制贪吃蛇的方向,按F3加速,F4减速\n");
        SetPos(30, 15);
        printf("加速能够得到更高的分数\n");
        SetPos(38, 20);
        system("pause");
        system("cls");
    }

  • WelcomeToGame 函数打印欢迎界面和游戏说明,等待用户按下任意键继续。
    // 绘制地图
    void CreateMap()
    {
        // 上
        printf("🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣");
        // 下
        SetPos(0, 26);
        printf("🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣");
        // 左
        int i = 0;
        for (i = 1; i <= 25; i++)
        {
            SetPos(0, i);
            printf("🤣");
        }
        // 右
        for (i = 1; i <= 25; i++)
        {
            SetPos(56, i);
            printf("🤣");
        }
    }

  • CreateMap 函数绘制游戏地图,使用字符 🤣 表示墙壁。
  • 蛇的初始化和移动
    void InitSnake(pSnake ps)
    {
        int i = 0;
        pSnakeNode cur = NULL;
        for (i = 0; i < 5; i++)
        {
            cur = (pSnakeNode)malloc(sizeof(SnakeNode));
            if (cur == NULL)
            {
                perror("malloc fail!");
                exit(1);
            }
            cur->next = NULL;
            cur->x = POS_X + 2 * i;
            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);
            printf("😋");
            cur = cur->next;
        }
    
        // 设置贪吃蛇的属性
        ps->dir = RIGHT;
        ps->score = 0;
        ps->food_weight = 10;
        ps->sleep_time = 200; // 单位是毫秒
        ps->status = OK;
    }

  • InitSnake 函数初始化蛇,使用头插法创建一个长度为 5 的蛇链表,并设置蛇的初始方向、分数、食物权重和速度。
    // 创造食物
    void CreateFood(pSnake ps)
    {
        int x = 0;
        int y = 0;
        // 生成的 x 是二的倍数
    again:
        do
        {
            x = rand() % 53 + 2;
            y = rand() % 25 + 1;
        } while (x % 2 != 0);
        // 蛇身不能与食物冲突
        pSnakeNode cur = ps->_pSnake;
        while (cur)
        {
            if (x == cur->x && y == cur->y)
            {
                goto again;
            }
            cur = cur->next;
        }
        // 创建食物节点
        pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));
        if (pFood == NULL)
        {
            perror("malloc fail!");
            exit(1);
        }
        pFood->x = x;
        pFood->y = y;
        pFood->next = NULL;
        SetPos(x, y);
        printf("🍔");
        ps->_pFood = pFood;
    }

  • CreateFood 函数随机生成食物的位置,确保食物不会出现在蛇身上,并在指定位置打印食物。
    // 初始化游戏
    void GameStart(pSnake ps)
    {
        // 1. 设置窗口大小,标题以及光标隐藏
        system("chcp 65001");
        system("mode con cols=100 lines=30");
        system("title \xcc\xb0 \xb3\xd4 \xc9\xdf");
        HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
        // 隐藏光标操作
        _CONSOLE_CURSOR_INFO CursorInfo;
        GetConsoleCursorInfo(houtput, &CursorInfo); // 获取控制台光标信息
        CursorInfo.bVisible = false; // 隐藏控制台光标
        SetConsoleCursorInfo(houtput, &CursorInfo); // 设置控制台光标状态
    
        // 2. 打印欢迎界面,功能介绍
        WelcomeToGame();
        // 3. 绘制地图
        CreateMap();
        // 4. 创建蛇
        InitSnake(ps);
        // 5. 创建食物
        CreateFood(ps);
    }
  • GameStart 函数完成游戏的初始化工作,包括设置窗口大小、标题、隐藏光标,打印欢迎界面,绘制地图,创建蛇和食物。
  • 游戏运行和结束
    // 运行游戏
    void GameRun(pSnake ps)
    {
        // 打印帮助信息
        PrintHelpInfo();
        do
        {
            // 打印总分数和食物的总分值
            SetPos(64, 8);
            printf("总分数:%d", ps->score);
            SetPos(64, 9);
            printf("当前食物分数:%2d", ps->food_weight);
            // 检测按键情况
            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_NORMAL;
            }
            else if (KEY_PRESS(VK_F3))
            {
                // 加速
                if (ps->sleep_time > 80)
                {
                    ps->sleep_time -= 30;
                    ps->food_weight += 2;
                }
            }
            else if (KEY_PRESS(VK_F4))
            {
                // 减速
                if (ps->food_weight > 2)
                {
                    ps->sleep_time += 30;
                    ps->food_weight -= 2;
                }
            }
            SnakeMove(ps); // 贪吃蛇走一步的过程
            Sleep(ps->sleep_time);
        } while (ps->status == OK);
    }

  • GameRun 函数是游戏的主循环,负责打印帮助信息、分数信息,检测用户按键,控制蛇的移动,并根据蛇的状态判断游戏是否结束。
    // 游戏善后的工作
    void GameEnd(pSnake ps)
    {
        SetPos(24, 12);
        switch (ps->status)
        {
        case END_NORMAL:
            printf("您主动结束游戏\n");
            break;
        case KILL_BY_SELF:
            printf("您撞到自己,游戏结束\n");
            break;
        case KILL_BY_WALL:
            printf("您撞到墙上,游戏结束\n");
            break;
        }
    
        // 打印死蛇
        pSnakeNode prev = ps->_pSnake;
        while (prev)
        {
            SetPos(prev->x, prev->y);
            printf("😫");
            prev = prev->next;
        }
        // 释放蛇身的链表
        pSnakeNode cur = ps->_pSnake;
        while (cur)
        {
            pSnakeNode prev = cur;
            cur = cur->next;
            free(prev);
        }
    }

  • GameEnd 函数负责游戏结束后的善后工作,包括打印游戏结束信息,将蛇的表情改为 😫,并释放蛇身链表的内存。
  • 总结

    通过对这个贪吃蛇游戏代码的详细解析,我们可以看到如何使用 C 语言和 Windows API 来实现一个简单的控制台游戏。代码中使用了链表来表示蛇的身体,通过控制光标的位置来实现游戏界面的更新。同时,代码还处理了用户输入、游戏状态判断、内存管理等问题,是一个很好的学习示例。

《餐馆点餐管理系统——基于Java和MySQL的课程设计解析》 在信息技术日益发达的今天,餐饮行业的数字化管理已经成为一种趋势。本次课程设计的主题是“餐馆点餐管理系统”,它结合了编程语言Java和数据库管理系统MySQL,旨在帮助初学者理解如何构建一个实际的、具有基本功能的餐饮管理软件。下面,我们将深入探讨这个系统的实现细节及其所涉及的关键知识点。 我们要关注的是数据库设计。在“res_db.sql”文件中,我们可以看到数据库的结构,可能包括菜品表、订单表、顾客信息表等。在MySQL中,我们需要创建这些表格并定义相应的字段,如菜品ID、名称、价格、库存等。此外,还要设置主键、外键来保证数据的一致性和完整性。例如,菜品ID作为主键,确保每个菜品的唯一性;订单表中的顾客ID和菜品ID则作为外键,与顾客信息表和菜品表关联,形成数据间的联系。 接下来,我们来看Java部分。在这个系统中,Java主要负责前端界面的展示和后端逻辑的处理。使用Java Swing或JavaFX库可以创建用户友好的图形用户界面(GUI),让顾客能够方便地浏览菜单、下单。同时,Java还负责与MySQL数据库进行交互,通过JDBC(Java Database Connectivity)API实现数据的增删查改操作。在程序中,我们需要编写SQL语句,比如INSERT用于添加新的菜品信息,SELECT用于查询所有菜品,UPDATE用于更新菜品的价格,DELETE用于删除不再提供的菜品。 在系统设计中,我们还需要考虑一些关键功能的实现。例如,“新增菜品和价格”的功能,需要用户输入菜品信息,然后通过Java程序将这些信息存储到数据库中。在显示所有菜品的功能上,程序需要从数据库获取所有菜品数据,然后在界面上动态生成列表或者表格展示。同时,为了提高用户体验,可能还需要实现搜索和排序功能,允许用户根据菜品名称或价格进行筛选。 另外,安全性也是系统设计的重要一环。在连接数据库时,要避免SQL注入攻击,可以通过预编译的PreparedStatement对象来执行SQL命令。对于用户输入的数据,需要进行验证和过滤,防止非法字符和异常值。 这个“餐馆点餐管理系统”项目涵盖了Java编程、数据库设计与管理、用户界面设计等多个方面,是一个很好的学习实践平台。通过这个项目,初学者不仅可以提升编程技能,还能对数据库管理和软件工程有更深入的理解。在实际开发过程中,还会遇到调试、测试、优化等挑战,这些都是成长为专业开发者不可或缺的经验积累
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值