前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【C语言项目】贪吃蛇(上)

【C语言项目】贪吃蛇(上)

作者头像
s-little-monster
发布2024-06-06 20:57:11
1030
发布2024-06-06 20:57:11
举报

一、目标

使用C语言在Windows环境的控制台中模拟实现贪吃蛇游戏 功能: ①贪吃蛇地图 ②蛇的移动 ③蛇吃食物 ④蛇撞墙或者撞到自己死亡 ⑤计算得分 ⑥暂停游戏 ⑦加速蛇身 ⑧计算得分

二、需要的知识

函数、枚举、结构体、动态内存管理、预处理指令、链表、Win32 API 其中只有Win32 API我们在之前的博文中没有提到过,这里我们一起着重学习一下

1、Win32 API概念

Application Programming Interface(应用程序编程接口),简称API函数。 Windows是一个多作业系统,它除了协调应用程序的执行、分配内存、管理资源之外,也是一个很大的服务中心,调用这个服务中心的各种服务,其中每一种服务就是一个函数,可以帮应用程序达到开启视窗、描绘图形、使用周边设备等目的,这些函数服务的对象就是应用程序Application Win32 API就是Windows32位平台的应用程序编程接口

2、控制台程序

在控制台能够执行的命令在编译器中也可以使用system函数来执行

(1)mode命令
代码语言:javascript
复制
mode con cols=100 lines=30;
代码语言:javascript
复制
#include <stdio.h>
int main()
{
    //设置控制台的大小为三十行一百列
    system("mode con cols=100 lines=30");
    //设置cmd窗⼝名称
    system("title 贪吃蛇"); 
    return 0;
}
(2)COORD坐标

COORD是Windows API中的一个结构体,表示一个字符在控制台屏幕上的坐标,左上角的坐标为(0,0)

COORD中有两个short值,一个为x,一个为y

代码语言:javascript
复制
typedef struct _COORD
{
    SHORT X;
    SHORT Y;
}COORD,*PCOORD;
代码语言:javascript
复制
COORD p = {10,20};//给坐标赋值
(3)隐藏光标的操作

在游戏的过程中,我们需要打印蛇身,墙壁,食物等,当我们在进行游戏时,是看不到光标闪烁的,但光标是存在的,所以我们要将它们隐藏

①GetStdHandle

GetStdHandle也是一个Windows API函数,它用于从一个特定的设备(标准输入、标准输出、标准错误)中获得一个句柄,使用这个句柄可以操作设备

代码语言:javascript
复制
HANDLE GetStdHandle(DWORD nStdHandle);
②GetConsoleCursorlnfo

检索有关指定控制台屏幕缓冲区的光标大小和可见性的信息

代码语言:javascript
复制
BOOL WINAPI GetConsoleCursorInfo
(
    HANDLE hConsoleoutput,
    PCONSOLE_CURSOR_INFO lpConsoleCursorInfo
);
//PCONSOLE_CURSOR_INFO是指向CONSOLE_CURSOR_INFO结构的指针

使用:

代码语言:javascript
复制
HANDLE houtput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值)
houtput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(houtput, &CursorInfo);//获取控制台光标信息
③CONSOLE_CURSOR_INFO

包含有关控制台光标的信息,是个结构体

代码语言:javascript
复制
typedef struct _CONSOLE_CURSOR_INFO 
{
    DWORD dwSize;//光标填充单元格的百分比,此值介于1-100,光标外观会发生变化
    BOOL bVisible;//游标的可见性,如果游标可见,其为TRUE,不可见为FALSE
} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;
④SetConsoleCursorInfo

设置指定控制台屏幕缓冲区的光标大小和可见性

代码语言:javascript
复制
BOOL WINAPI SetConsoleCursorInfo
(
    HANDLE hConsoleoutput,
    const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo
);

使用:

代码语言:javascript
复制
HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
//影藏光标操作
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(houtput, &CursorInfo);//获取控制台光标信息
CursorInfo.bVisible = false; //隐藏控制台光标
SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态
⑤SetConsoleCursorPosition

设置指定控制台屏幕缓冲区中的光标位置

代码语言:javascript
复制
BOOL WINAPI SetConsoleCursorPosition
(
    HANDLE hConsoleoutput,
    COORD pos
);

我们将想要设置的坐标信息放在pos当中,调用该函数将光标位置设置到指定的位置 使用:

代码语言:javascript
复制
COORD pos = {10, 5};
HANDLE houtput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值)
houtput = GetStdHandle(STD_OUTPUT_HANDLE);
//设置标准输出上光标的位置为pos
SetConsoleCursorPosition(hOutput, pos);
封装一个函数命名为SetPos,专门用来设置光标位置
代码语言:javascript
复制
void SetPos(short x, short y)
{
    COORD pos = { x, y };
    HANDLE houtput = NULL;
    //获取标准输出的句柄(⽤来标识不同设备的数值)
    houtput = GetStdHandle(STD_OUTPUT_HANDLE);
    //设置标准输出上光标的位置为pos
    SetConsoleCursorPosition(houtput, pos);
}
(4)GetAsyncKeyState

原型:

代码语言:javascript
复制
SHORT GetAsyncKeyState
(
    int vKey
);

这个函数可以通过检测游戏进行时的按键来传递相应的返回值,可以实时监测此时按键的状态,调用后,如果该键按下返回的short值的最高位为1,否则为0,所以我们定义一个宏,来判断该键是否被按下

代码语言:javascript
复制
#define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )

括号里是虚拟键代码,填入相应的虚拟键代码可以检测键是否被按下

三、游戏设计与分析

1、地图

在游戏中墙体、蛇体以及食物的打印用的是宽字符,占用两个字符,因为一个字符的话游戏会相当不美观,因为x轴的1和y轴的1的长度不同,y轴的1的长度约等于x轴的1的两倍

x=y=1时:

x=2,y=1时:

我们可以明显看出格子的区别 在宽字符打印之前我们要进行本地化

(1)本地化

包含头文件<local.h>

(2)类项

①LC_COLLATE:影响字符串比较函数strcoll和strxfrm ②LC_CTYPE:影响字符处理函数的行为 ③LC_MONETARY:影响货币格式 ④LC_NUMERIC:影响printf的数字格式 ⑤LC_TIME:影响时间格式strftime和wcsftime ⑥LC_ALL:以上所有类别全部修改

(3)setlocale
代码语言:javascript
复制
char* setlocale (int category, const char* locale);

该函数用来修改当前地区 第一个 第二个参数仅定义了两种可能的取值: “C”正常模式 “ ”本地模式 切换到本地模式就可以支持宽字符的打印

代码语言:javascript
复制
setlocale(LC_ALL, "C");//正常模式
setlocale(LC_ALL, " ");//本地模式
(4)宽字符的打印

可以把宽字符看做是长的字符

代码语言:javascript
复制
#include <stdio.h>
#include <locale.h>
int main()
{
setlocale(LC_ALL, "");
wchar_t c = L's';
wchar_t s = L"little_monster";
wprintf(L"%lc",c);
wprintf(L"%ls",s);
}
(5)地图坐标

这里我们实现的地图是27*58的,围绕边缘打印一周墙体

2、蛇身和食物

开始时,假设蛇长度为5,蛇身的每个节点用◆符号,在一个固定的连续5个坐标放蛇身 蛇的每个节点的x坐标都是2的倍数(宽字符),否则会出现一半节点在里边一半节点在墙里 食物节点的x坐标也是2的倍数,并且坐标不能与蛇身重合

3、数据结构设计

游戏运行过程中,蛇每吃掉一个食物蛇体就增加一个节点,每个节点就存储当前的坐标以及下一个节点的坐标

代码语言:javascript
复制
typedef struct SnakeNode
{
    int x;
    int y;
    struct SnakeNode* next;
}SnakeNode, * pSnakeNode;

现在我们有了节点的定义,我们再来用一个结构体维护整条贪吃蛇以及食物

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

蛇的方向我们用枚举,因为蛇的行进方向只能有一个,使用枚举可以减小内存的占用

代码语言:javascript
复制
enum DIRECTION
{
 UP = 1,
 DOWN,
 LEFT,
 RIGHT
};

游戏状态我们也用枚举,理由同上,状态只能有一个

代码语言:javascript
复制
enum GAME_STATUS
{
 OK,//正常运⾏
 KILL_BY_WALL,//撞墙
 KILL_BY_SELF,//咬到⾃⼰
 END_NOMAL//正常结束
};

4、设计游戏流程

当我们把整个框架定好,下一步就可以来实现游戏了

下一篇再见~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-05-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、目标
  • 二、需要的知识
    • 1、Win32 API概念
      • 2、控制台程序
        • (1)mode命令
        • (2)COORD坐标
        • (3)隐藏光标的操作
        • (4)GetAsyncKeyState
    • 三、游戏设计与分析
      • 1、地图
        • (1)本地化
        • (2)类项
        • (3)setlocale
        • (4)宽字符的打印
        • (5)地图坐标
      • 2、蛇身和食物
        • 3、数据结构设计
          • 4、设计游戏流程
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档