C语言实现简单打字游戏
一、效果图和源码
1.先看运行效果图
2.给出源码。
#include<stdio.h>
#include<time.h>
#include<ctype.h>
#include<windows.h>
#include<conio.h>#define NUM 16 //字串总数(页面能存在的最大字串数) struct Block{char strings[20]; //用于存储字串,20是最大长度 int x; //记录字串的横坐标 int y; //记录字串的纵坐标 int Color[20]; //记录每个字符的颜色
}block[NUM];enum Difficulty_Level{Difficulty = 2, Medium = 3, Easy = 4
} speed; //难度等级,字串移动速度,单位秒,由玩家给出 int n = 0; //通过的字串个数
int k = 0; //一个字串输入正确的字符数
double t1, t2; //t用来计时 ,用时间计时方式进行字串下移 void window(int w, int h); //设置窗口大小 void HideCursor(void); //隐藏光标 void color(int c);void gotoxy(int x, int y);void StartInterface(void);void InitInterface(void);void InitBlock(void);void Startgame(void);void JudgeString(int i); void UpdateBlock(int i);void MoveBlock(void);void OtherChoice(int choice); //暂停,重开,结束游戏的选择 void Gameover(void);int UpdateScore(void);void window(int w,int h) //设置窗口大小
{HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);//获取标准输出的句柄,命令行的程序会把字符输出到屏幕上。COORD size = {w, h};//设置窗口的大小。SetConsoleScreenBufferSize(hOut, size);//重新设置缓冲区大小。SMALL_RECT rc = {1,1, w, h};//重置窗口位置和大小。SetConsoleWindowInfo(hOut ,true ,&rc);//重置窗口大小system("cls");//清理屏幕return;
}void HideCursor(void) //隐藏光标
{CONSOLE_CURSOR_INFO curInfo; //定义光标信息的结构体变量curInfo.dwSize = 1; //如果没赋值的话,隐藏光标无效curInfo.bVisible = FALSE; //将光标设置为不可见HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台句柄SetConsoleCursorInfo(handle, &curInfo); //设置光标信息
}void color(int c) // 改变字体颜色
{SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c);/* 0=黑色 8=灰色 1=蓝色 9=淡蓝色 2=绿色 10=淡绿色 3=湖蓝色 11=淡浅绿色 4=红色 12=淡红色 5=紫色 13=淡紫色 6=黄色 14=淡黄色 7=白色 15=亮白色 */
}void gotoxy(int x, int y) //设置光标位置
{COORD xy;xy.X=x;xy.Y=y;SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),xy);
}void StartInterface(void) //提供难度选择
{system("cls");system("color 07");n = 0; //重置n值,为使再玩一次时n为0 color(7); gotoxy(30, 12);printf("欢迎来到 “打 字 游 戏 ”!");gotoxy(30, 15);printf("请选择游戏难度:");gotoxy(30, 18);printf("1.简单 2.中等 3.困难");gotoxy(47, 15);char ch = getchar();while(ch != '1' && ch != '2' && ch != '3'){fflush(stdin);gotoxy(47, 15);for(int i = 0; i < 30; i++)putchar(' ');gotoxy(47, 15);color(12);printf("输入不合法,请重新输入!");color(7);Sleep(1000);gotoxy(47, 15);for(int i = 0; i < 30; i++)putchar(' ');gotoxy(47, 15);ch = getchar(); }fflush(stdin);if(ch == '1')speed = Easy;if(ch == '2')speed = Medium;if(ch == '3')speed = Difficulty;InitInterface(); //初始化游戏界面
}void InitInterface(void)
{system("cls"); const int x = 80, y = 30;for(int i = 0; i < x-1; i++){for(int j = 0; j < y; j++){if(i == 0 || i == x-2 || j == 0 || j == y-1 || i == x-30){gotoxy(i, j);printf("■");}} } for(int i = 50; i < 80; i++){gotoxy(i, 13);printf("■");}color(14);gotoxy(55, 2);printf("通过个数: %d 个", n);gotoxy(55, 4);printf("暂停: 空格");gotoxy(55, 6);printf("暂停后任意键继续");gotoxy(55, 8);printf("重新开始: Tab");gotoxy(55, 10);printf("结束游戏: ESC"); gotoxy(55, 16);printf("游戏说明:");gotoxy(59, 18);printf("在字串下落之前,正");gotoxy(59, 20);printf("确输入字串所有字符");gotoxy(59, 22);printf("可得一分"); gotoxy(55, 25);color(4);if(speed == Easy)printf("当前难度: 简单");else if(speed == Medium)printf("当前难度: 中等");elseprintf("当前难度: 困难");InitBlock(); //初始化字串
}void InitBlock(void)
{srand(unsigned(time(NULL)));const int size = 5; //初始长度为5 for(int i = 0; i < NUM; i++){for(int j = 0; j < size; j++){block[i].strings[j] = 97 + rand() % (122 - 97 + 1); // a ——z 的ASSCI序列是 97 ——122block[i].x = 2 + rand() % 43; //x表示字串的初始横坐标 block[i].y = -2*i - 3; //y表示纵坐标block[i].Color[j] = 7; //将所有字符颜色置为 7(白色) }block[i].strings[size] = '\0'; //构成字符串,才能使用strlen()函数 }Startgame(); //游戏主体函数
}void Startgame(void) //主体函数 (包含主要算法)
{t1 = clock(); //开始计时 while(1){ int t = 25000 * speed; //经测试25000下运行时间大约为1秒 while(--t){if(kbhit() != 0) //敲击了键盘break; } //未敲击键盘时,出循环,t 为 0 if(t != 0) //敲击了键盘,找到要输入的字串,通过首字符确定要输入的字串 {char ch = getch();if(ch == 32 || ch == 9 || ch == 27) //暂停,退出,重开 {OtherChoice(ch);continue;}/*遍历所有的字串,寻找距离底线最近的首字符相符的字串*/int index[NUM], ans, y_max = 0, j = 0; //前两变量用于记录下标 for(int i = 0; i < NUM; i++){if(0 < block[i].y && block[i].y < 30 && block[i].strings[0] == ch){index[j] = i; //记录字串下标 j++;}}int next = 0;while(j) //存在符合条件的首字符时,j不为0 {j--;next = 1; //运行了该while()则必然找到了符合要求的字串,才能进行下一步操作 if(y_max < block[index[j]].y){ans = index[j]; //记录最优下标 y_max = block[index[j]].y;} }if(next == 1) {k = 1; //正确输入了一个字符 /*先输出第一个字符,再对该字串验证后续输入的正确性*/block[ans].Color[0] = 10; color(block[ans].Color[0]);gotoxy(block[ans].x, block[ans].y);putchar(block[ans].strings[0]);JudgeString(ans); //判断后续字串输入的正确性 }fflush(stdin); // 敲错后刷新缓冲区,防止乱敲键盘,导致程序一直运行上述代码 }t2 = clock();if((t2 - t1) / CLOCKS_PER_SEC > speed) //时间间隔超过 SPEED 秒,字串下移 MoveBlock();}
}void JudgeString(int i) //主体函数 (包含主要算法)
{while(1) { t2 = clock();if((t2 - t1) / CLOCKS_PER_SEC > speed) //超时,字串下移 MoveBlock();int t = 25000 * speed; while(--t){if(kbhit() != 0) //敲击了键盘break; } //未敲击键盘时,出循环后,t 为 0 if(t != 0) //以下为敲击键盘的情况,未敲击时直接继续死循环 {char ch = getch();if(ch == 32 || ch == 9 || ch == 27) //暂停,退出,重开 {OtherChoice(ch);continue;}if(0 < block[i].y && block[i].y < 30 && block[i].strings[k] == ch) //下一个字符输入正确 {block[i].Color[k] = 10; color(block[i].Color[k]);gotoxy(block[i].x + k, block[i].y);putchar(block[i].strings[k]);k++; }else //输入错误,则该字串重置, 即将整个字串的颜色改为白色 {for(int j = 0; j < k; j++){block[i].Color[j] = 7; color(block[i].Color[j]);gotoxy(block[i].x + j, block[i].y);putchar(block[i].strings[j]);} k = 0; //k也重置, 即正确的字符为0个 break; }int size = strlen(block[i].strings);if(k == size) //完全正确,用空格消除字串 {putchar('\a'); //蜂鸣gotoxy(block[i].x, block[i].y);while(k > 0){putchar(' ');k--; //退出循环后,k恢复到 0 }n++; //正确数加一 color(15); gotoxy(65, 2);printf("%d", n);UpdateBlock(i); //更新该字串 break;} }}
}void MoveBlock(void) //字串下移
{ /*先删除原位置,再进行下移*/ for(int i = 0; i < NUM; i++){int size = strlen(block[i].strings);if(block[i].y > 0) /* 用空格替代原字串,以实现下次循环时字串的下移 */ {gotoxy(block[i].x, block[i].y);while(size > 0){putchar(' ');size--;}}block[i].y += 2; //下移 }for(int i = 0; i < NUM; i++){if(block[i].y > 30) //字串超过底线,游戏结束 Gameover();if(block[i].y > 0) //字串可以输出 {int size = strlen(block[i].strings);for(int j = 0; j < size; j++){gotoxy(block[i].x + j, block[i].y);color(block[i].Color[j]);putchar(block[i].strings[j]);} }}t1 = clock(); //重新计时
}void UpdateBlock(int i) //更新字串
{int size = 5 + n / 7; //难度系数,每对7个,单个字串的字符数加一 int last_y = 0; //寻找排在最后的字串for(int order = 0; order < NUM; order++)last_y = last_y < block[order].y ? last_y : block[order].y; for(int j = 0; j < size; j++){block[i].Color[j] = 7; //初始颜色为白色 block[i].strings[j] = 97 + rand() % (122 - 97 + 1); // a ——z 的ASSCI序列是 97 ——122block[i].x = 2 + rand() % (48 - size); //x表示字串的初始横坐标 block[i].y = last_y - 2; //将更新后的字串排在最后 } block[i].strings[size] = '\0'; //构成字符串,才能使用strlen()函数
}void OtherChoice(int choice)
{switch(choice){case 32: //暂停 system("pause>nul"); //暂停 t1 = clock(); //暂停结束后重新计时 break;case 27: //退出Gameover();break; default: //重新开始StartInterface();}
} void Gameover(void)
{if(kbhit())fflush(stdin);system("cls");gotoxy(28, 14);color(3);printf("游 戏 结 束 !");Sleep(1200);int max = UpdateScore(); //记录最高分,写到文件中,并返回最终的最高分 system("cls");system("color 7C");int i = 0; /* 绘制爱心 */ for (float y = 1.3f; y > -1.3f; y -= 0.1f, i++) {gotoxy(7, i);for (float x = -1.5f; x < 1.5f; x += 0.05f) {float a = x * x + y * y - 1;putchar(a * a * a - x * x * y * y * y <= 0.0f ? '*' : ' ');}putchar('\n');}gotoxy(27, 24);printf(" 得 分 : %d ", n);gotoxy(27, 26);printf(" 最 高 分 : %d ", max);double a1, a2; //计时a1 = clock();while(1) //使爱心频闪 {a2 = clock();if((a2 - a1) / CLOCKS_PER_SEC > 1.8) //频闪1.8秒后{gotoxy(29, 28);printf("按任意键继续"); } Sleep(100);system("color 7E");Sleep(100);system("color 7B");Sleep(100);system("color 79");Sleep(100);system("color 7D");Sleep(100);system("color 74");Sleep(100);system("color 76");Sleep(100);system("color 71");Sleep(100);system("color 72");Sleep(100);system("color 74");if(kbhit()){char ch = getch();break;}}gotoxy(29, 28);printf("再来一次? Y / N : ");char ch = getchar();ch = tolower(ch);while(ch != 'y' && ch != 'n'){fflush(stdin);gotoxy(49, 28);for(int i = 0; i < 30; i++)putchar(' ');gotoxy(49,28);printf("输入不合法,请重新输入!");Sleep(1000);gotoxy(49, 28);for(int i = 0; i < 30; i++)putchar(' ');gotoxy(49, 28);ch = getchar();ch = tolower(ch); }fflush(stdin);if(ch == 'y')StartInterface();if(ch == 'n')exit(1);
}int UpdateScore(void) //记录最高分,写到文件中,并返回最终的最高分
{int score[3];FILE *fp = fopen("打字游戏最高分记录.txt", "r"); //只读if(fp == NULL) //创建新文件并初始化成绩 {fp = fopen("打字游戏最高分记录.txt", "w+"); //创建文件,可读写,但会清除原文件 for(int i = 0; i < 3; i++)score[i] = 0;} elsefor(int i = 0; i < 3; i++)fscanf(fp, "%d", &score[i]);fclose(fp);fp = fopen("打字游戏最高分记录.txt", "r+"); //读写 if(speed == Easy) //简单{score[0] = score[0] > n ? score[0] : n; for(int i = 0; i < 3; i++)fprintf(fp, "%d ", score[i]);fputs("\n上面分别为三种难度系数的最高分,不要乱改!!!,若修改后导致程序运行错误\n\
或者最高分为乱码数字,将此.txt文件永久删除即可。", fp);fclose(fp);return score[0];}if(speed == Medium) //中等 {score[1] = score[1] > n ? score[1] : n; for(int i = 0; i < 3; i++)fprintf(fp, "%d ", score[i]);fputs("\n上面分别为三个难度系数的最高分,不要乱改!!!", fp);fclose(fp);return score[1];}if(speed == Difficulty) //困难 {score[2] = score[2] > n ? score[2] : n; for(int i = 0; i < 3; i++)fprintf(fp, "%d ", score[i]);fputs("\n上面分别为三个难度系数的最高分,不要乱改!!!", fp);fclose(fp);return score[2];}
}int main(void)
{ window(80, 30); //设置窗口尺寸,意思是设置一个x = 80, y = 30尺寸的cmd窗口HideCursor(); //隐藏光标StartInterface(); //询问游戏难度环节 return 0;
}
二、核心算法
由运行效果图可以看出,算法核心在于字串的下落、玩家输入正确性和字串之间的交互,以及两者之间的结合。
1.字串的下落可以用过计时控制,字串下落前,使用t1 = clock()开始计时,完成一些操作后,使用t2 = clock(), if( (t2-t1) / CLOCKS_PER_SEC > t), 字串下落。意思是如果运行这一些操作后,时间间隔大于t秒,就让字串下落。
2.玩家输入的正确性判断时,读取玩家输入的第一个字符,寻找在游戏窗口中第一个字符符合且距离底线最近的字串,然后单独把这个字串拿出来,判断玩家后续输入的正确性。
3.字串下落和玩家输入的结合则可以用kbhit()函数和getch()函数配合使用,前者判断是否敲击了键盘,后者直接读取敲击的字符而无需按回车键。算法实现:如果玩家敲击了键盘,就读取输入然后寻找匹配的字串,并验证输入的正确性,期间不断计时,根据时间间隔下移字串。
三、代码结构讲解
1.头文件,宏,全局变量
#include<stdio.h>
#include<time.h>
#include<ctype.h>
#include<windows.h>
#include<conio.h>#define NUM 16 //字串总数(页面能存在的最大字串数) struct Block{char strings[20]; //用于存储字串,20是最大长度 int x; //记录字串的横坐标 int y; //记录字串的纵坐标 int Color[20]; //记录每个字符的颜色
}block[NUM];enum Difficulty_Level{Difficulty = 2, Medium = 3, Easy = 4
} speed; //难度等级,字串移动速度,单位秒,由玩家给出 int n = 0; //通过的字串个数
int k = 0; //一个字串输入正确的字符数
double t1, t2; //t用来计时 ,用时间计时方式进行字串下移
(1)头文件
1.<time.h> 中使用了time()来获取随机数,clock()用于计时;
2.<ctype.h> 中使用了tolower(), 将大写字母变成小写;
3.<windows.h> 中使用了控制台句柄来实现改变光标位置和改变颜色,即color()和gotoxy()函数,同时还使用了system()函数中的清屏、改变颜色(前景加背景)、屏幕冻结等功能,使用Sleep()来实现睡眠;
4.<conio.h> 主要用到了getch(),该函数的作用是不按回车直接读取键盘输入,kbhit()用于判断是否敲击键盘。
(2)宏
全局一个宏NUM,表示字串的数量。给定数值16的原因是游戏窗口高度为30,每个字串的间隔为1,为了让字串占满窗口,设置为16。
(3)全局变量
结构体block代表一个字串,总共16个;
枚举speed表示三种难度;
int n表示输入正确的字串数;
int k表示单个字串中,输入正确的字符数;所以 n 和 k 需要一开始就置位0;
double t1, t2 用于计时,此处不能用int类型,因为返回的时间是带小数点的。
2.辅助功能函数:
这四个函数只起辅助作用,会贯穿整个代码,但是其本身没有什么算法。所以后续不会做详细介绍
void window(int w, int h); //设置窗口大小 void HideCursor(void); //隐藏光标 void color(int c); //改变字体颜色 void gotoxy(int x, int y); //设置光标位置
3.main()函数:
main()函数很简单,在整个代码的最下方。
功能: 设置一个窗口,隐藏光标,和打开整个游戏的入口函数,即StartInterface()函数。
int main(void)
{ window(80, 30); //设置窗口尺寸,意思是设置一个x = 80, y = 30尺寸的cmd窗口 HideCursor(); //隐藏光标StartInterface(); //询问游戏难度环节 return 0;
}
4.void StartInterface(void) 询问游戏难度
使用fflush()(清除缓冲区)优化输入,防止输入错误,和缓冲区残留字符干扰游戏,最后通过InitInitface(),进行游戏界面初始化。初始化比较简单就不单独介绍,直接跳过了。
void StartInterface(void) //提供难度选择
{system("cls"); //清屏system("color 07"); //黑底白字n = 0; //重置n值,为使再玩一次时n为0 color(7); gotoxy(30, 12);printf("欢迎来到 “打 字 游 戏 ”!");gotoxy(30, 15);printf("请选择游戏难度:");gotoxy(30, 18);printf("1.简单 2.中等 3.困难");gotoxy(47, 15);char ch = getchar();while(ch != '1' && ch != '2' && ch != '3'){fflush(stdin);gotoxy(47, 15);for(int i = 0; i < 30; i++)putchar(' ');gotoxy(47, 15);color(12);printf("输入不合法,请重新输入!");color(7);Sleep(1000);gotoxy(47, 15);for(int i = 0; i < 30; i++)putchar(' ');gotoxy(47, 15);ch = getchar(); }fflush(stdin);if(ch == '1')speed = Easy;if(ch == '2')speed = Medium;if(ch == '3')speed = Difficulty;InitInterface(); //初始化游戏界面
}
5.void InitBlock(void)初始化字串
使用srand()用时间作为种子,再通过rand()获取随机数,字串的横坐标为随机数,纵坐标出初始为负数,是因为窗口的纵坐标范围是0——30,并让字串的纵坐标依次排列,最后再初始化字串每个字符的颜色。
注意字串必须构成字符串,因为后续需要使用到strlen()函数。
void InitBlock(void)
{srand(unsigned(time(NULL)));const int size = 5; //初始长度为5 for(int i = 0; i < NUM; i++){for(int j = 0; j < size; j++){block[i].strings[j] = 97 + rand() % (122 - 97 + 1); // a ——z 的ASSCI序列是 97 ——122block[i].x = 2 + rand() % 43; //x表示字串的初始横坐标 block[i].y = -2*i - 3; //y表示纵坐标block[i].Color[j] = 7; //将所有字符颜色置为 7(白色) }block[i].strings[size] = '\0'; //构成字符串,才能使用strlen()函数}Startgame(); //游戏主体函数
}
6. void Startgame(void) 游戏主体函数
1.总体思想:若玩家开始输入,捕获输入内容的第一个字符,然后去寻找第一个字符匹配的字串,若找到了,将这个字串单独拿出来,进行后续输入的判断,没有找到,则什么也不做,并不断重复这些操作直到游戏结束。
2.需要克服的问题: (1)玩家输入不需要按回车结束输入; (2)字串必须定时下落; (3) 玩家输入不能影响字串的下落。
3.算法实现:clock()函数返回时间; kbhit()函数当敲击键盘时返回非零值; getch()函数直接读取输入不需要回车键;
4.细节优化: 寻找字串时,需优先寻找距离下限最近的,因为当窗口中有两个首字符一样的字串时,玩家更期望先消除更下面的那个。
int t = 25000 * speed; //经测试25000下运行时间大约为1秒 while(--t){if(kbhit() != 0) //敲击了键盘break; }
使用while(--t)判断 t 遍是否有键盘输入,原因:假设只判断一遍,会立即返回0值(没有敲击键盘)。令t = 25000 * speed, 可以使当没有任何输入时,while(--t)大约运行 speed 秒, 就可以保证没有输入时,每经过 speed 秒字串下移。
void Startgame(void) //主体函数 (包含主要算法)
{t1 = clock(); //开始计时 while(1){ int t = 25000 * speed; //经测试25000下运行时间大约为1秒 while(--t){if(kbhit() != 0) //敲击了键盘break; } //未敲击键盘时,出循环,t 为 0 if(t != 0) //敲击了键盘,找到要输入的字串,通过首字符确定要输入的字串 {char ch = getch();if(ch == 32 || ch == 9 || ch == 27) //暂停,退出,重开 {OtherChoice(ch);continue;}/*遍历所有的字串,寻找距离底线最近的首字符相符的字串*/int index[NUM], ans, y_max = 0, j = 0; //前两变量用于记录下标 for(int i = 0; i < NUM; i++){if(0 < block[i].y && block[i].y < 30 && block[i].strings[0] == ch){index[j] = i; //记录字串下标 j++;}}int next = 0;while(j) //存在符合条件的首字符时,j不为0 {j--;next = 1; //运行了该while()则必然找到了符合要求的字串,才能进行下一步操作 if(y_max < block[index[j]].y){ans = index[j]; //记录最优下标 y_max = block[index[j]].y;} }if(next == 1) {k = 1; //正确输入了一个字符 /*先输出第一个字符,再对该字串验证后续输入的正确性*/block[ans].Color[0] = 10; color(block[ans].Color[0]);gotoxy(block[ans].x, block[ans].y);putchar(block[ans].strings[0]);JudgeString(ans); //判断后续字串输入的正确性 }fflush(stdin); // 敲错后刷新缓冲区,防止乱敲键盘,导致程序一直运行上述代码 }t2 = clock();if((t2 - t1) / CLOCKS_PER_SEC > speed) //时间间隔超过 SPEED 秒,字串下移 MoveBlock();}
}
7. void JudgeString(int i) 第二个主体函数
1.总体思想:在上一个函数中找到了第一个首字符匹配的字串,这个函数则将这个字串单独拿出来,继续判断后续输入的正确性。若后续输入完全正确,就消除该字串(空格覆盖),若出现错误,需要从第一个字符重新输入(从头重新输入),或者选择消除其他的字串。
2.算法实现:给出死循环,获取玩家输入并做出判断,全对或者出现错误,将字串颜色恢复为白色并退出死循环返回上一层函数。函数声明中的 int i 表示第 i 个字串。
void JudgeString(int i) //主体函数 (包含主要算法)
{while(1) { t2 = clock();if((t2 - t1) / CLOCKS_PER_SEC > speed) //超时,字串下移 MoveBlock();int t = 25000 * speed; while(--t){if(kbhit() != 0) //敲击了键盘break; } //未敲击键盘时,出循环后,t 为 0 if(t != 0) //以下为敲击键盘的情况,未敲击时直接继续死循环 {char ch = getch();if(ch == 32 || ch == 9 || ch == 27) //暂停,退出,重开 {OtherChoice(ch);continue;}if(0 < block[i].y && block[i].y < 30 && block[i].strings[k] == ch) //下一个字符输入正确 {block[i].Color[k] = 10; color(block[i].Color[k]);gotoxy(block[i].x + k, block[i].y);putchar(block[i].strings[k]);k++; }else //输入错误,则该字串重置, 即将整个字串的颜色改为白色 {for(int j = 0; j < k; j++){block[i].Color[j] = 7; color(block[i].Color[j]);gotoxy(block[i].x + j, block[i].y);putchar(block[i].strings[j]);} k = 0; //k也重置, 即正确的字符为0个 break; }int size = strlen(block[i].strings);if(k == size) //完全正确,用空格消除字串 {putchar('\a'); //蜂鸣gotoxy(block[i].x, block[i].y);while(k > 0){putchar(' ');k--; //退出循环后,k恢复到 0 }n++; //正确数加一 color(15); gotoxy(65, 2);printf("%d", n);UpdateBlock(i); //更新该字串 break;} }}
}
8.void MoveBlock(void) 字串下移 和 void UpdateBlock(int i)更新字串
1.字串下移采用先删除原字串,再在新位置输出字串。删除使用空格覆盖,新位置则在空格覆盖完后,所有字串y轴 + 2的方式。每次移动完一次字串重新计时。
2.更新字串时先遍历一边所有字串,找到排在最末尾的那个字串,然后更新的字串就放在最末尾的后面,即last_y - 2,其他跟初始化字串一样。
void MoveBlock(void) //字串下移
{ /*先删除原位置,再进行下移*/ for(int i = 0; i < NUM; i++){int size = strlen(block[i].strings);if(block[i].y > 0) /* 用空格替代原字串,以实现下次循环时字串的下移 */ {gotoxy(block[i].x, block[i].y);while(size > 0){putchar(' ');size--;}}block[i].y += 2; //下移 }for(int i = 0; i < NUM; i++){if(block[i].y > 30) //字串超过底线,游戏结束 Gameover();if(block[i].y > 0) //字串可以输出 {int size = strlen(block[i].strings);for(int j = 0; j < size; j++){gotoxy(block[i].x + j, block[i].y);color(block[i].Color[j]);putchar(block[i].strings[j]);} }}t1 = clock(); //重新计时
}void UpdateBlock(int i)
{int size = 5 + n / 7; //难度系数,每对7个,单个字串的字符数加一 int last_y = 0; //寻找排在最后的字串for(int order = 0; order < NUM; order++)last_y = last_y < block[order].y ? last_y : block[order].y; for(int j = 0; j < size; j++){block[i].Color[j] = 7; //初始颜色为白色 block[i].strings[j] = 97 + rand() % (122 - 97 + 1); // a ——z 的ASSCI序列是 97 ——122block[i].x = 2 + rand() % (48 - size); //x表示字串的初始横坐标 block[i].y = last_y - 2; //将更新后的字串排在最后 } block[i].strings[size] = '\0'; //构成字符串,才能使用strlen()函数
}
9. void OtherChoice(int choice) 其他选择(暂停、结束游戏、重新开始)
代码中 sysytem("pause") ;的效果如下:
而 system("pause>nul") 可以消除 “请按任意键继续. . .” 这几个字。
void OtherChoice(int choice)
{switch(choice){case 32: //暂停 system("pause>nul"); //暂停 t1 = clock(); //暂停结束后重新计时 break;case 27: //退出Gameover();break; default: //重新开始StartInterface();}
}
10. void Gameover(void) 游戏结束
游戏结束后需要更新一下最高分记录,然后可以整一些花里胡哨的东西来美化一下界面。在下面代码中使用了绘制爱心并使爱心频闪的方式。
右边是爱心的数学函数公式,其算法实现是构造一个矩形,从上至下,从左至右,在该数学函数公式范围内的输出 ‘ * ’, 否则输出空格。
让爱心频闪的算法是,使用 Sleep(100), 每睡眠100 ms,更换一次字体颜色。
void Gameover(void)
{if(kbhit())fflush(stdin);system("cls");gotoxy(28, 14);color(3);printf("游 戏 结 束 !");Sleep(1200);int max = UpdateScore(); //记录最高分,写到文件中,并返回最终的最高分 system("cls");system("color 7C");int i = 0; /* 绘制爱心 */ for (float y = 1.3f; y > -1.3f; y -= 0.1f, i++) {gotoxy(7, i);for (float x = -1.5f; x < 1.5f; x += 0.05f) {float a = x * x + y * y - 1;putchar(a * a * a - x * x * y * y * y <= 0.0f ? '*' : ' ');}putchar('\n');}gotoxy(27, 24);printf(" 得 分 : %d ", n);gotoxy(27, 26);printf(" 最 高 分 : %d ", max);double a1, a2; //计时a1 = clock();while(1) //使爱心频闪 {a2 = clock();if((a2 - a1) / CLOCKS_PER_SEC > 1.8) //频闪1.8秒后{gotoxy(29, 28);printf("按任意键继续"); } Sleep(100);system("color 7E");Sleep(100);system("color 7B");Sleep(100);system("color 79");Sleep(100);system("color 7D");Sleep(100);system("color 74");Sleep(100);system("color 76");Sleep(100);system("color 71");Sleep(100);system("color 72");Sleep(100);system("color 74");if(kbhit()){char ch = getch();break;}}gotoxy(29, 28);printf("再来一次? Y / N : ");char ch = getchar();ch = tolower(ch);while(ch != 'y' && ch != 'n'){fflush(stdin);gotoxy(49, 28);for(int i = 0; i < 30; i++)putchar(' ');gotoxy(49,28);printf("输入不合法,请重新输入!");Sleep(1000);gotoxy(49, 28);for(int i = 0; i < 30; i++)putchar(' ');gotoxy(49, 28);ch = getchar();ch = tolower(ch); }fflush(stdin);if(ch == 'y')StartInterface();if(ch == 'n')exit(1);
}
11. int UpdateScore(void) 记录最高分,写到文件中,并返回最终的最高分
此函数主要用到了打开文件的一些方法,下面罗列了打开文件的各种方式,就不多加赘述了
int UpdateScore(void) //记录最高分,写到文件中,并返回最终的最高分
{int score[3];FILE *fp = fopen("打字游戏最高分记录.txt", "r"); //只读if(fp == NULL) //创建新文件并初始化成绩 {fp = fopen("打字游戏最高分记录.txt", "w+"); //创建文件,可读写,但会清除原文件 for(int i = 0; i < 3; i++)score[i] = 0;} elsefor(int i = 0; i < 3; i++)fscanf(fp, "%d", &score[i]);fclose(fp);fp = fopen("打字游戏最高分记录.txt", "r+"); //读写 if(speed == Easy) //简单{score[0] = score[0] > n ? score[0] : n; for(int i = 0; i < 3; i++)fprintf(fp, "%d ", score[i]);fputs("\n上面分别为三种难度系数的最高分,不要乱改!!!,若修改后导致程序运行错误\n\
或者最高分为乱码数字,将此.txt文件永久删除即可。", fp);fclose(fp);return score[0];}if(speed == Medium) //中等 {score[1] = score[1] > n ? score[1] : n; for(int i = 0; i < 3; i++)fprintf(fp, "%d ", score[i]);fputs("\n上面分别为三个难度系数的最高分,不要乱改!!!", fp);fclose(fp);return score[1];}if(speed == Difficulty) //困难 {score[2] = score[2] > n ? score[2] : n; for(int i = 0; i < 3; i++)fprintf(fp, "%d ", score[i]);fputs("\n上面分别为三个难度系数的最高分,不要乱改!!!", fp);fclose(fp);return score[2];}
}
C语言实现简单打字游戏相关推荐
- 用c语言做一个五子棋程序,C语言制作简单五子棋游戏
原标题:C语言制作简单五子棋游戏 C语言制作简单的五子棋游戏 学习C语言的人很多,但是用C语言很少,而用来为自己所用,来做游戏的人就更少了,很多人都是跟着学校学习,学校讲到哪就坐到哪,但是以后却还是不 ...
- 弹球小程序怎么用c语言编写,C语言实现简单弹球游戏
电视机待机的屏幕上的弹球,怎么实现? 今天文章就跟大家分享下C语言实现简单弹球游戏的具体代码,供大家参考,具体内容如下 #include #include #include #include #inc ...
- 基于C语言的简单飞机游戏
最近在B站上看到一位老师用VC++6.0编写游戏的教程,自己尝试了一下十分有意思,这里是链接:[C语言/EasyX]做游戏,学编程 C语言/EasyX游戏开发[自取课程资料,评论区置顶]_哔哩哔哩_b ...
- Pygame 简单打字游戏
功能描述: 1.点击开始游戏,会出现一段英文文章,并进入60s倒计时 2.如果一分钟内输入完成这段会自动呈现下一段 3.单词正确数实时统计,背景颜色随输入速度而变化 代码: 注意:上面动态图需要放在游 ...
- html简单打字游戏,javascript实现简单打字游戏
本文实例为大家分享了javascript打字游戏的具体代码,供大家参考,具体内容如下 传智打字游戏 .label{ position:absolute;left: 0px; } var CODE = ...
- html简单打字游戏,javascript实现的简单打字游戏
传智打字游戏 .label{ position:absolute;left: 0px; } var CODE = "QWERTYUIOPASDFGHJKLZXCVBNM"; var ...
- Java 实现简单打字游戏
打字游戏 题目: 代码实现: 题目: 编写一个打字游戏,从一个面板顶端随机出现一些字母,字母按一定的时间自顶向下移动,如果字母接触到面板底端则Game Over,用户可以通过敲击键盘消除移动中的字母. ...
- 一个简单打字游戏的设计(C语言)
需求: 生成的20个随机字符串由大小写组成,规则如下: 程序源码: #include<stdio.h> #include<stdlib.h> #include<time. ...
- 简单五子棋游戏c语言简单,C语言制作简单五子棋游戏
#pragma comment(lib,'winmm.lib') //玩游戏 void PlayGame() { //鼠标操作 int chess[N][N] = { 0 };//标志没有棋子的标志 ...
- 如何编辑简单打字游戏
先来简单看一下要求 一,我们可以先编写Player类,先把属性定义好,然后进行封装 public class Player {private String name; //定义姓名private in ...
最新文章
- 为什么我喜欢EJB 3.0并且尤其喜欢EJB 3.1
- CodeForces - 1327D Infinite Path(图论综合)
- 通杀IIS7.0畸形解析0day漏洞
- linux系统内存缓冲和磁盘预留
- CI框架 -- URL
- UnityShader10:CG标准函数库
- 5个开发人员不应该错过的最好跨平台PHP编辑器
- C#中在窗体间使用消息来处理相关联的事件
- ArcGIS相关学习视频链接
- js获取浏览器信息及版本(兼容IE)
- java zip 中文文件名乱码_java使用zip压缩中文文件名乱码的解决办法
- 机器学习算法对比分析(转载)
- 看了B站上的这些Java视频,我飘了!
- 《大象 Thinking in UML》学习笔记(三)——UML核心元素之参与者、用例
- 百度网盘微信小程序文件 同步到百度网盘APP或客户端
- 每日LeetCode一道题————有效的数独
- 京东股权众筹投后总结和反思2(冲动不要紧,有后悔药)
- 精美卡通儿童教育班会课件PPT模板
- 进入知识储备期的通知
- 怎么查看linux有多少内存插槽,linux 查看内存插槽数、最大容量和频率
热门文章
- www.idcnd.net传媒官方客服提供
- android手机脱网分析,网络营销-13款手机浏览器分析(Android).pptx
- 嵌入式linux下的触屏模拟
- [转载]STED和STORM、PALM
- EclipseMaven导入Maven项目后在pom.xml出现Missing artifact org.springframework:spring-jdbc:jar:3.2.4.RELEAS
- Unity3d常用快捷键
- [TJOI2019]唱、跳、rap和篮球_生成函数_容斥原理_ntt
- 磁盘基础知识-磁盘的构成
- 所谓的飞扬档案管理软件
- 面试之防火墙软硬件架构