公开课的准备(二)
是否坚定了学生学习C/C++的念头,我并不确定,但有一点我可以确定,那就是,我的公开课还有一部分没有完成,那就是一个能够提起学生兴趣并能够比较快速实现的小游戏。
我不想一开始就打开vc++,然后拿着一个现成的代码就开始演示或讲解代码,那估计会有很多学生晕头转向,甚至会睡着。
到底该怎么来讲解这个游戏程序呢?于我来说,这是个再简单不过的小程序了,坐在那里不出半个小时,就能顺利完成,但学生能从你的书写里面收获多少东西呢?如果能做到即便学生听不懂看不懂你的程序,但依然有所收获,我就积了功德了。教师这个工作,是个良心活,我既然看不起应付教学,草草了事的老师,那就一定不能成为这样的老师。因此,我开始思索我八年来做项目的一些经验总结,希冀从里面能获得一些有用的灵感。
很快,我想定了一个方案,那就是以一个真正的项目开发流程的模式讲解这个游戏。当然,要把一个项目的整个开发流程都讲解完,也不太现实。因为只项目运作过程,就有大量的知识量,那不是短时间能讲完的。更何况,这远偏离了公开课的初衷,更亵渎了学生们抽出时间来听课的原因,使得一个原本有意义的公开课也变得可有可无了。因此,我思定之后,决定抽出一部分项目开发内容,串成线,自然地去讲解出来。
既然当做项目来讲,首当其冲,我们要分析项目需求,公司接一个项目,很多时候,用户本身都不清楚他要的东西应该包含什么功能,他甚至会把他想要的东西用一句话告诉你。大多情况下,他们是根据你的引导,或者看到相似的产品,不停地提出新的需求。而我们要做的,就是根据用户本身的业务范围,对他的功能内容进行细化,然后和用户敲定细节,这就是简单的项目需求分析了。需求在一个项目的整个生存期,很可能会出现这样那样的改变,但一个项目如果频繁的出现需求的变更,那要么可能是前期的需求分析出了问题,要么一定是前期需求分析出了问题。说到这,我记得我曾看到一个调侃程序员话,说:杀死一个程序员,不需要用枪或刀,只需要把需求变更三遍就可以了。这反映了一个现实情况,现实情况就是很多项目的需求分析都难以做到位。因此导致了需求的不停变更,最痛苦的,莫过于程序员了,因为任何一个需求的变动,客户都不会让你延长交付时间为代价。而根据需求更改代码,有时候是伤筋动骨,甚至连架构都要重构,这就是为啥程序员不停地加班工作的主要原因之一。以至于竟然有美女在网上发言:宁给捡破烂的当小三,也不嫁给程序员。这很伤程序员的自尊,但现实几乎真是这样的。所以,一般来说,单身几乎是程序员的标识,如果一个人脱离了单身,那他基本也快要脱离程序员了。我想,这应该就是中国程序员的悲催现状,也是为啥说程序员职业是青春饭的原因吧。
我起初似乎大概也许只说了一句话:编写一个打字练习游戏。现在我们来细化需求:
既然是打字游戏,那一定是从键盘输入字符和程序中的字符进行匹配的过程了。既然是游戏,那一定得有“通关文碟”,即过关条件,这里的条件,肯定是正确率了。很自然,我们亦能想到,每关的打字速度应该不一样。当然,程序中生成的字符应该是随机生成,否则玩游戏的人摸到了规律,还有意愿继续玩下去么?再有就是,用户在玩的过程中,可能会随时退出该游戏,而做其他事情,我们不能不让他退出。
通过上面的分析,我想我们会很快给出如下几个需求:
1、 程序每次生成的字符是随机生成的。
2、 设定游戏的关数。
3、 控制游戏的速度。
4、 设定通关的正确率
5、 设定程序退出方法。
项目需求分析完成之后,我们接下来就要做软件需求分析。这里会生成一个文档,叫《软件需求规格说明书》。很显然,项目需求分析也会生成一个文档《项目需求分析说明书》,他的目的之一也是为程序员提供书写《软件需求规格说明书》的依据。
软件需求分析,和项目需求分析不一样,项目的需求分析是为了弄清楚用户的功能需求,而软件需求分析则是对每个项目需求的进一步细化,细化到什么程度合适呢?这每个公司并不一样,但有两个条件可以作为判断依据:1、细化到能够进行项目架构,指导后续《软件设计说明书》的编写。2、细化到测试人员可以用它来清晰准确的写出系统测试用例。因此,《软件规格设计说明书》应该是项目开发中非常重要的一环。它的成败,直接影响到项目的成败。
我们这里也来对项目需求进行进一步细化。
针对字符的随机生成,确定生成方法和生成范围。你总不能超越键盘之外吧,另外,你生成的东西也得要屏幕能显示出来吧。针对游戏的关数,我们的设计不能过少,过少了,用户一下子就通关了,感觉没意思,也不能过多,多了用户可能玩的过程中,玩反感了,干脆就不玩了。同样,游戏的速度也不能太慢,每关也应该有一些稍微明显的速度变化。把一个有些的速度设计到快的没人能完成,那这个项目也算失败了。太慢,当然也不行,你不能鄙视人的能力吧。既然是打字游戏,我们的目的就是让用户能熟练的使用键盘,因此,正确率必须是合适的设定。总不能让用户正确不到一半就算通关吧。每次吃个半饱就让你干活,你愿意么?程序当然要能退出,否则用户忍不住,说不定会过来敲你的头,万一受不了掐死你也正常。有些软件故意设定不让用户退出,想以此来提高它的市场占有率,结果却得不偿失,就是这个原因。耍流氓的结果最终是被流氓耍。
综上,我们得出如下的软件需求。
1、在100 *60的范围内随机生成一个随机字符,只生成小写a~~z的26个英文字符。
2、游戏的关卡设定为10关。
3、第一关字符屏显时间为1秒,每过一关,屏显时间减少100毫秒。
4、每关随机生成字符次数为100个,准确率达到95%进入下一关。
5、当用户通关或者在键盘上输入大写’Q’,则推出游戏。
好了,软件需求分析完成了,接下来我们就要设计我们的程序算法了。
即我们该进入《软件设计说明书》的编写了,确切的说,这里是两部分:
《软件概要设计说明书》和《软件详细设计说明书》,但很多公司将这两个揉合到一起了,统称为《软件设计说明书》。这重点突出在了设计上,通过软件设计说明书,我们应该能完全准确的架构一个项目,指定项目的每个模块功能实现。搞定了软件设计说明书,项目可以说已经完成了一半了。好的软件设计说明书能够涵盖代码90%的内容,可以这么说,真正的编码程序员只需要根据设计说明书的要求,完成编码即可。算法甚至都不用思考,因此,“码农”一词,说的甚是亲切。毫不客气的说,刚进入程序界的,几乎都是码农,相当码农也非易事。
好了,根据软件规格说明书,我们可以的得到如下内容。
函数一:界面显示
函数名 | DWORD WINAPI DispUI(LPVOID lp) |
主调函数 | Main()函数 |
调用函数 | 随机数函数rand();休眠函数sleep(); |
函数功能 | Windows线程回调函数,实现字符在屏幕上的动态显示 |
入参 | 必选参数:LPVOID lp;回调函数进入参数 |
出参 | 无 |
函数返回值 | DWORD |
函数描述:
Begin:
用数组定义整个游戏屏幕的范围;
设定时间作为随即生成字符的种子。
While(字符不是游戏退出字符)
{
随机生成一个字符;
让其在随机指定的位置开始下落
查看用户从界面输入的字符是否和随机生成的字符一致
If(一致)
{
正确统计值自增1;
重新循环生成下一个字符
}
Else if(不一致)
{
循环生成下一个字符;
}
判断时间是否达到规定时间
If(到达规定时间)
{
If(正确率不达标)
继续该级别的训练
Else
进入下一关的训练;
}
Else
{
继续本关的训练
}
设定各个字符产生的频率;
}
End:
函数二:入口函数
函数名 | int main(void) |
主调函数 | 系统调用 |
调用函数 | 线程创建函数,退出字符获取函数 |
函数功能 | 函数入口,用来创建打字游戏处理线程 |
入参 | 无 |
出参 | 无 |
函数返回值 | Int |
函数描述:
Begin
调用CreateThread函数创建线程,线程的执行函数为DispUI;
While(用户没有输入退出字符)
{
等待用户输入新字符。
}
End
项目生成:
Keyword.exe
至此,我们可以在设计说明书的帮助下进入到了编码阶段,具体的编码内容,如下:
/*********************************************************************
**文件名称:main.c
**文件功能:实现一个简单的打字游戏
**函数列表:DispUI(LPVOID lp)
main()
**创建时间:2012-11-11
**创 建 人:new create by LLQ
**********************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>
#include<conio.h>
#include<time.h>
#define WIDTH 60 //屏幕的最大宽度
#define HEIGHT 100 //屏幕的最大高度
charg_chInput; //用户输入字符信息;
int nRightNumber = 0; //统计正确的输入个数。
int nTotalNumber = 0; //统计总共的字母个数;
int nSleepMultime = 1000; //最慢设定为1秒
float RightPercent= 0.0f; //正确率
int nQuit = 0;
/*********************************************************************
**函数名称:DWORD WINAPI DispUI(LPVOID lp)
**函数功能:线程执行函数,设定UI界面宽度,并产生下落字符
**函数参数:LPVOID lp
**创建时间:2012-11-11 Create by LLQ
**********************************************************************/
DWORD WINAPIDispUI(LPVOID lp)
{
char Layout[HEIGHT][WIDTH]; //屏幕显示
char Ch;
char Row, Col;
int i, j;
srand((unsigned int)time(NULL));
while(g_chInput != 'Q')
{
Ch =rand()%26 + 'a'; //只处理小写字母;
Row = 0; //从0行开始
Col =rand() % WIDTH; //生成该字母的下落位置。
//休眠时间
++nTotalNumber;
//该循环用来设置屏幕中每个位置的显示字符
for(Row=0; Row<HEIGHT; ++Row)
{
for(i=0; i<HEIGHT; ++i)
{
for(j=0; j<WIDTH; ++j)
{
Layout[i][j] = ' ';
}
}
//每次下落一行,同一列的不同行保存该字符,相当于,每次下落一层。
Layout[Row][Col] = Ch;
system("cls"); //系统函数,用来清屏;
//该循环吧下落到指定位置的字符打印出来,其他位置打印为空。
for(i=0; i<HEIGHT; ++i)
{
for(j=0; j<WIDTH; ++j)
{
printf("%c",Layout[i][j]);
}
printf("\n");
}
//键盘输入的字符和随机生成的字符是否一致,一致,则生成下一个字符。
if(g_chInput == Ch)
{
++nRightNumber;
if(50 == nTotalNumber)
{
//每到100个字符,统计正确率,正确率达到95%,则加快速度
RightPercent =nRightNumber / nTotalNumber;
if(RightPercent >0.95)
{
printf("速度升级,加油哦!\n");
Sleep(2000);
nSleepMultime -=100;//设定管卡为10关
if(nSleepMultime>= 100)
{
//重新开始统计新一关的正确率
RightPercent =0;
nRightNumber =0;
nTotalNumber =0;
}
else
{
printf("恭喜你,你的输入级别已经达到熟练!\n");
Sleep(2000);
nQuit = 1;
return 0;
}
}
else
{
printf("抱歉,你本关不过关,需要重新修炼\n");
Sleep(2000);
RightPercent = 0;
nRightNumber = 0;
nTotalNumber = 0;
}
}
break;
}
Sleep(nSleepMultime);
}
}
return 0;
}
int main(void)
{
CreateThread(NULL, 0, DispUI, NULL, 0,NULL);
//退出条件成立则退出该程序
while(1 != nQuit || g_chInput != 'Q')
{
g_chInput = getch();
}
return 0;
}
至此,我的公开课准备完成。