2025-06-24 11:51:02
来源:新华网
目录
1.介绍:
(1).控制台程序(Console)(也就是控制控制台窗口的长和宽)
(2).控制光标位置
(3).GetStdHandle函数:
(4).GetConsoleCursorInfo函数:
(5).SetConsoleCursorInfo函数:
(6).SetConsoleCursorPosition函数:
(7).GetAsyncKeyState:
2.宽字符打印与本地化设置:
(1).前情提要:
(2).本地化介绍:本地化:
(3).类项:
(4).setlocale函数:设置本地化状态
(5).宽字符的打印:
3.贪吃蛇游戏的实现:
(1).test.c文件:
(2).snake.h
(3).snake.c
Hello everyone,今天我来教大家实现一下贪吃蛇游戏的相关操作:
首先呢,我们大家在写贪吃蛇代码前要先了解一些知识点(Win32 API技术),在这里,我就带着大家一起来看一下吧:
Windows这个多作业系统除了协调应用程序的执行等操作,他同时也是一个很大的服务中心,这个服务中心它包含着很多种类的服务,其实这些服务就是有一系列的函数组成的,换句话说,就是我们现在在这个电脑上所完成的一系列步骤,其实在都是由函数来实现的,Windows规定这些函数我们都可以其直接使用,比如说打开视窗,描绘图形,使用周边设备等功能,也就是Windows的每一个函数他都能完成一个特定的功能,因为这些函数服务的对象是应用程序(Applicancation),因此简称API函数,Win32 API也就是Microsoft Windows 32位平台的应用程序编程接口。那么,接下来,我就来给大家介绍几种实现我们的贪吃蛇游戏需要用到的函数吧。
但我们想要去改变控制台的长和宽的时候,mode命令去实现这个功能,例如:mode con coles=100 lines=30,当我们在Windows系统控制台上输入这个命令时,然后按Enter键那么这个时候控制台就会变成长100,宽30。
我们也可以通过title命令去改变控制台窗口的名字,例如:title 贪吃蛇,当我们在Windows系统控制台上输入这个命令时,然后按Enter键那么这个时候控制台的题目就会变成贪吃蛇(只仅限于是在程序没结束时控制台的题目一直都会使贪吃蛇)。
注意:mode命令和title命令不可以直接在C语言中使用,但是我们可以使用system()函数来执行这一命令,代码如下所示:
system("mode con cols=100 lines=30");
system("title 贪吃蛇");
调试控制台屏幕如图所示:调试控制台屏幕变成100行30列,而且题目变成贪吃蛇。
在了解这个知识点前,我们首先要知道控制台屏幕其实就是一个坐标轴,而其中的每一个位置就是那个白点一样是一个坐标,
我们要想让光标定位(注意:现在只是定位,不是转变到这个位置)到某一个坐标位置,在Win32 API上面有一个结构体:COORD结构体:typedef struct _COORD{
SHORT x;
SHORT y;
}COORD,*PCOORD;例如:COORD pos={10,30};//给这个结构体x赋值10,y赋值30,换句话说,pos定位到(10,30)这个坐标位置。
用于从一个特定的标准设备(标准输入(例如键盘),标准输出(屏幕),标准错误)中获得一个句柄(换句话说,就是你想要去操作一个设备,你就得去获得这个设备的一些数据,(比如说:你现在想要去使用锅,那么你就得抓住这口锅的手柄,这样你才能去操作这口锅)这里的句柄和锅的手柄是一个道理,当我们得到这个句柄之后,我们就可以去操作这个设备了)。
HANDLE GetStdHandle (DWORD nStdHandle);//(HANDLE是句柄的类型,DWORD是GetStdHandle参数的类型,nStdHandle是参数的取值)
{STD_INPUT_HANDLE 标准输入设备(输入缓冲区的控制台)
nStdHandle的取值{STD_OUTPUT_HANDLE 标出输出设备(控制台屏幕缓冲区)
{STD_ERROR_HANDLE 标准错误设备(控制台屏幕缓冲区)
例如:HANDLE output=GetStdHandle(STD_OUTPUT_HANDLE);//定义了一个句柄output里面有标准输出是设备的一些信息。
检索有关指定控制台屏幕缓冲区的光标大小和可见性的信息(获得光标信息)。
BOOL WINAPT GetConsoleCursorInfo(HANDLE nStdHandle,PCONSOLE_CURSOR_INFO IpConsoleCursorInfo);//(nStdHandle句柄,PCONSOLE_CURSOR_INFO是指向CONSOLE_CURSOR_INFO结构体的指针,该结构接收有关主机游标(光标)的信息
typedef struct _CONSOLE_CURSOR_INFO{
DWORD dwSize;//光标的大小
BOOL bVisible;//光标的显示度
};CONSOLE_CURSOR_INFO,*PCONSOLE_CURSOR_INFO;)。
例如:我想要获得控制台屏幕中光标的信息
HANDLE houtput=GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_IN cursor_info=;
GetConsoleCursorInfo(houtput,&cursor_info);//将houtput句柄中有关光标的大小和显示度这两个信息放到cursor_info数组里面。(注意:这个函数仅仅只是获得信息,并不是改变)
设置指定控制台屏幕缓的光标大小和可见度。
BOOL WINAPT SetConsoleCursorInfo(HANDLE nStdHandle,const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo);//(nStdHandle句柄,CONSOLE_CURSOR_INFO *lpConsoleCursorInfo是指向CONSOLE_CURSOR_INFO结构体的指针)
例如: HANDLE houtput=GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_IN cursor_info=;
GetConsoleCursorInfo(houtput,&cursor_info);
cursor_info.bVisible=false;
SetConsoleCursorInfo(houtput,&cursor_info);//将cursor_info中的光标信息在控制台屏幕中展现出来。
设置光标位置。
BOOL WINAPT SetConsoleCursorInfo(HANDLE nStdHandle,COORD dwCurPosition);//(nStdHandle句柄,dwCurPosition是COORD类型的结构体
typedef struct _COORD{
SHORT x;
SHORT y;
}COORD,*PCOORD;)
例如: HANDLE houtput=GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos{10,20};
SetConsoleCursorPosition(houtput,pos);//将屏幕中光标的位置定位到pos中所指定的位置。
获取键盘中各键的状态,它会返回一个16位的short类型的数据,如果返回值的最低位被置为1则说明,该键被按过,否则为0。
SHORT GetAsyncKeyState(int vkey);//(每个键都有一个对应的虚拟键值,再此函数中就是指vkey)
例如:我想要判断一下键5是否被按过:
#define AK(vk) ((GetAsyncKeyState(vk)&0x1)?1:0)//这里我们需要注意一下,GetAsyncKeyState函数它的返回值的16位的值是不确定的,但是我们只需要去判断它的最后是不是1或者是0即可。基于这种原因,我们让GetAsyncKeyState函数的返回值和0x1&,也就是如下列代码所示:比如说GetAsyncKeyState函数返回了值是
(函数返回的随机值)& 00101010 01010101 | & 00101010 01010100
00000000 00000001 | 00000000 00000001
最后的结果为 00000000 00000001 | 00000000 00000000
此头文件中所提供的函数用于控制C语言库中对于不同的地区会产生不一样行为的部分。换句话说,就是这个C语言在最开始的时候它是由美国人创造的,因此它仅限与使用英语地区的人使用,但是当你本地化之后,你就可以在屏幕上打印出汉字(我们的汉字占两个坐标位置(也就是可以打印宽字符))。
通过上述解释,相信大家对于本地化已经有了一些了解,但是怎么修改呢?
比如:LC_ALL------针对所有类项进行修改
LC_TIME----只影响时间的格式(类项不只有这两个,这里时间有限,就不给大家一一去介绍了,大家感兴趣的话可以去自行了解)
char*setlocale(int category,const char*locale);//(category这个参数指的就是上述类项其中的一种,针对第二个参数,标准只给了两种取值"C"(正常模式(也就是英语模式))和""(本地模式))。
例如: setlocale(LC_ALL,"");//将所有的类项均转换为本地模式。
当我们将其转化为本地模式后,那么此时的编译器按逻辑来说是可以打印出汉字的,但还是要做一些处理,所用到的函数是什么,占位符是什么。
我们在打印宽字符的时候只能使用wprintf函数,若打印的是宽字符,则所需占位符为%Lc,若为宽字符串,则所需占位符为%Ls。
例如: setlocale(LC_ALL,"");
wprintf(L"%Lc",L'比');//比,占两个坐标位置
wprintf(L"%Ls",L'进击的巨人');//进击的巨人
OK好的,同志们,经过了上述知识点的解释,现在以我们的知识储备量就可以开始我们的游戏了。
#include "snake.h"//完成的是游戏的测试逻辑void test(){ int ch = 0; do { system("cls"); Snake snake = { 0 }; stratgame(&snake); rungame(&snake); //finishgame(&snake); setpos(20, 15); wprintf(L"再来一局吗?(Y/N):"); ch = getchar(); while (getchar() != '\n'); //system("cls"); } while (ch == 'y' || ch == 'Y'); setpos(0, 27);}int main(){ //设置适配本地环境 setlocale(LC_ALL, ""); srand((unsigned int)time(NULL)); test(); return 0;}
#pragma once#define _CRT_SECURE_NO_WARNINGS 1#include #include #include #include #include #include #define POS_X 24#define POS_Y 5#define WALL L'□'#define BODY L'●'#define FOOD L'★'enum DIRECTION{ UP = 1, DOWN, LEFT, RIGHT};//蛇的状态enum GAME_STATUS{ OK, //正常 KILL_BY_WALL, //撞墙 KILL_BY_SELF, //撞到自己 END_NORMAL //正常退出};//蛇身的节点类型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;//定位光标位置void setpos(short x, short y);//游戏的初始化void stratgame(pSnake ps);//打印环境页面和功能介绍void welcomesnake();//创建地图void createmap();//创建蛇void createsnake(pSnake ps);//创建食物void createfood(pSnake ps);//运行游戏void rungame(pSnake ps);//打印帮助信息void printhelpinfo();//游戏暂停void pause();//贪吃蛇移动void movesnake(pSnake ps);//吃食物void eatfood(pSnakeNode newnode, pSnake ps);//不是食物void nofood(pSnakeNode newnode, pSnake ps);//碰到墙壁void killbywall(pSnake ps);//碰到自己void killbyself(pSnake ps);//结束游戏void finishgame(pSnake ps);
#include "snake.h"//创建食物void createfood(pSnake ps){ int x = 0; int y = 0;again: 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 food = (pSnakeNode)malloc(sizeof(SnakeNode)); if (food == NULL) { perror("createfood()::malloc()"); return 1; } food->x = x; food->y = y; food->next = NULL; setpos(x, y); wprintf(L"%lc", FOOD); ps->_pFood = food;}//创建蛇void createsnake(pSnake ps){ pSnakeNode cur = NULL; for (int i = 0; i < 5; i++) { cur = (pSnakeNode)malloc(sizeof(SnakeNode)); if (cur == NULL) { perror("createsnake()::malloc()"); return 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); wprintf(L"%lc", BODY); cur = cur->next; } //设置贪吃蛇的属性 ps->_dir = RIGHT; ps->_food_weight = 10; ps->_score = 0; ps->_sleep_time = 200; ps->_status = OK;}//创建地图void createmap(){ int i = 0; for (i = 0; i < 29; i++) { wprintf(L"%lc", WALL); } for (i = 1; i <= 25; i++) { setpos(0, i); wprintf(L"%lc", WALL); } for (i = 1; i <= 25; i++) { setpos(56, i); wprintf(L"%lc", WALL); } setpos(0, 26); for (i = 0; i < 29; i++) { wprintf(L"%lc", WALL); }}//定位光标位置void setpos(short x, short y){ HANDLE houtput = NULL; houtput = GetStdHandle(STD_OUTPUT_HANDLE); COORD pos = { x,y }; SetConsoleCursorPosition(houtput, pos);}//打印环境页面和功能介绍void welcomesnake(){ setpos(40,14); wprintf(L"%ls",L"欢迎来到贪吃蛇小游戏n"); setpos(42, 14); system("pause"); system("cls"); setpos(25,14); wprintf(L"%ls", L"用↑.↓.←.→ 来控制蛇的移动,按F3加速,F4减速n"); setpos(25,15); wprintf(L"%ls", L"加速能得到更好的分数n"); setpos(42, 20); system("pause"); system("cls");}//游戏开始 void stratgame(pSnake ps){ system("mode con cols=100 lines=30"); system("title 贪吃蛇"); HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_CURSOR_INFO cursorinfo; GetConsoleCursorInfo(houtput, &cursorinfo); cursorinfo.bVisible = false; SetConsoleCursorInfo(houtput, &cursorinfo); //打印环境页面和功能介绍 welcomesnake(); //创建地图 createmap(); //创建蛇 createsnake(ps); //创建食物 createfood(ps);}//判断按键是否被按#define KEY_PRESS(vk) ((GetAsyncKeyState(vk)&1)?1:0)//游戏暂停void pause(){ while (1) { Sleep(200); if (KEY_PRESS(VK_SPACE)) { break; } }}//碰到自己void killbyself(pSnake ps){ pSnakeNode cur = ps->_pSnake->next; while (cur) { if (cur->x == ps->_pSnake->x && cur->y == ps->_pSnake->y) { ps->_status = KILL_BY_SELF; break; } cur = cur->next; }}//碰到墙壁void 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; }}//打印帮助信息void printhelpinfo(){ setpos(64, 14); wprintf(L"%ls", L"不能穿墙,不能咬到自己"); setpos(64, 15); wprintf(L"%ls", L"用 ↑. ↓ . ← . → 来控制蛇的移动"); setpos(64, 16); wprintf(L"%ls", L"按F3加速,F4减速"); setpos(64, 17); wprintf(L"%ls", L"按ESC退出游戏,按空格暂停游戏"); setpos(64, 18); wprintf(L"%ls", L"郑源博制作");}//不是食物void nofood(pSnakeNode newnode, pSnake ps){ newnode->next = ps->_pSnake; ps->_pSnake = newnode; pSnakeNode cur = ps->_pSnake; while (cur->next->next!=NULL) { setpos(cur->x, cur->y); wprintf(L"%lc", BODY); cur = cur->next; } setpos(cur->next->x, cur->next->y); printf(" "); free(cur->next); cur->next = NULL;}//吃食物void eatfood(pSnakeNode newnode, pSnake ps){ ps->_pFood->next = ps->_pSnake; ps->_pSnake = ps->_pFood; free(newnode); newnode=NULL; pSnakeNode cur = ps->_pSnake; while (cur) { setpos(cur->x, cur->y); wprintf(L"%lc", BODY); cur = cur->next; } ps->_score += ps->_food_weight; createfood(ps);}//贪吃蛇移动void movesnake(pSnake ps){ pSnakeNode newnode = (pSnakeNode)malloc(sizeof(SnakeNode)); if (newnode == NULL) { perror("movesnake()::malloc()"); return 1; } switch (ps->_dir) { case RIGHT: newnode->x = ps->_pSnake->x + 2; newnode->y = ps->_pSnake->y; break; case LEFT: newnode->x = ps->_pSnake->x - 2; newnode->y = ps->_pSnake->y; break; case DOWN: newnode->x = ps->_pSnake->x; newnode->y = ps->_pSnake->y + 1; break; case UP: newnode->x = ps->_pSnake->x; newnode->y = ps->_pSnake->y - 1; break; } if (newnode->x == ps->_pFood->x && newnode->y == ps->_pFood->y) { eatfood(newnode,ps); } else { nofood(newnode, ps); } killbywall(ps); killbyself(ps);}//运行游戏void rungame(pSnake ps){ printhelpinfo(); do { setpos(64, 10); printf("总分数:%d\n", ps->_score); setpos(64, 11); printf("当前食物的分数:%2d\n", ps->_food_weight); if (ps->_dir != RIGHT && KEY_PRESS(VK_LEFT)) { ps->_dir = LEFT; } else if (ps->_dir != LEFT && KEY_PRESS(VK_RIGHT)) { ps->_dir = RIGHT; } else if (ps->_dir != UP && KEY_PRESS(VK_DOWN)) { ps->_dir = DOWN; } else if (ps->_dir != DOWN && KEY_PRESS(VK_UP)) { ps->_dir = UP; } 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; } } movesnake(ps); Sleep(ps->_sleep_time); } while (ps->_status == OK);}//结束游戏void finishgame(pSnake ps){ setpos(24, 12); switch (ps->_status) { case KILL_BY_WALL: wprintf(L"%ls", L"您撞到墙上,结束游戏n"); break; case KILL_BY_SELF: wprintf(L"%ls", L"您撞到自己,结束游戏n"); break; case END_NORMAL: wprintf(L"%ls", L"您主动结束游戏n"); break; } pSnakeNode cur = ps->_pSnake; while (cur) { pSnakeNode del = cur; cur = cur->next; free(del); }}