我想学着做一些小的游戏,比如俄罗斯方块,学那种语言比较好。
送你一个很好的学习网址:http://www.vbaspnew.com/
对于广大编程初学者来说,存在着这样普遍的认识:学习程序设计选择编程语言
很重要,开口闭口就是VisualBasic、Visual C++、Delphi、Power Builder等一些现在比较流行的开发工具。经常看见有初学者问:我学C/C++,究竟是学
VisualC++好,还是学Borland C++ Builder好呢?或者就是要求在Visual Basic和Delphi之间作出选择。学习程序设计和学习程序设计语言究竟是怎么一个关系?初学者究竟应该如何取舍呢?就此我们打个简单的比方:学习程序设计就好比学习射击,而程序设计语言就对应射击中的气枪、手枪、步枪等各种枪械。学习射击必须要选择一种枪械,不可能没有枪还能学好射击的,也不可能同时学会使用所有的枪械。但是,如果掌握一种枪械的射击,再学别的也就触类旁通了。因为在熟悉一种枪械的同时,也学习了射击技术本身。再学其他的,自然事半功倍。学习程序设计也是一样,必然要从学习一门程序设计语言开始入手。同时在学习的过程中?
建议:学习程序设计不必苛求一定要从某一种语言入手,可以在Visual Basic、Delphi、Borland C++ Builder或者DOS下的Pascal、Turbo C(或Borland C)中间选择自己比较容易接受的一种进行学习。从中主要学习的是编程本身的思想,学习的是用程序设计的眼光来看待问题。或许,学习的效果不能马上看到,但是可以学到扎实的基本功,而这对于将来的进一步发展是有很大好处的。这些基本功包括:
一、强化逻辑分析和建立数学模型的能力
编程的目的是什么?是要解决现实中的问题。而现实中的问题是用自然语言描述的,目前的计算机对其无法理解。我们要做的就是必须用计算机能理解的方式将问题“告诉”计算机。也就需要我们对现实问题进行逻辑分析,建立合理的数学模型,然后以程序和数据的形式输入计算机,R、G、B三色份量来各表示一种颜色,对于颜色的处理被转化为对R、G、B三个整数的处理。我们只要改变这三个整数,就可以将其改变成另外一种颜色。对于其它的现实问题,我们也要像
这样使其可以被计
二、数据结构和算法知识
程序设计发展到现在,已经有了一套比较普遍、完整的数学模型(我们称之为
数据结构)和对应这些数学模型的处理方法(也就是算法)。目前的程序就是以
此为基础的。在程序员中间流传着一句经验之谈:“好的数据结构是程序的一
半。”其重要性可见一斑。作为一个程序员
三、丰富的实践经验
程序设计本身就是一门实践性很强的学科。一个只存在于纸上的程序如同没
有翅膀的鸟永远不能飞翔在现实的天空。任由代码写得再巧妙,风格再优美,也
只是一张纸上的一连串字符而已。就好比是一份作文练习,或许写得很好,但是
不交给老师去批阅,和一张白纸又有什么区别呢?!最后,作为一个程序员还有一点小要求:那就是身体素质要好,能熬夜才行。
Top of Form 1
选其器与善其事——纵谈编程工具的选择选其器相同主题的文章
“工欲善其事,必先利其器”——《论语》。很多人在学习软件编程时,都希望
选择一款优秀的编程工具。到书店逛逛,可以看到各种琳琅满目的编程书籍,可
是你还没有想好选择学习什么编程语言,也就无所适从,不知道该买什么书看。
要想学好编程,必须认真了解各种语言的特点,找准自己的学习方向。那么你首
先必须明确你想要做什么,你所感兴趣的是什么?进一步说就是:你现在想要完
成哪一方面的程序设计,你的程序主要完成的功能是什么?由此在众多软件中,
选择出适合自己的开发工具。
一、基础语言开发工具
诸如Delphi、VC++、VB等语言都是一种面向对象的程序设计语言,虽然每种语言在对面向对象的支持不尽相同,但它们始终贯穿整个程序设计的思维方式的主线。因此,要学好这些语言最开始不应该将重点放在某种语言的具体语法格式上,而应该注重学习面向对象的基本理论
最容易的入门语言——Visual Basic
在掌握面向对象编程的基本概念的基础上,相对来讲,VB是比较容易入门的
一门语言。在学习VB时,应该注意抓住对象的精髓,避免凌乱和繁杂,注意掌握
对象和控件的属性和事件以及它们的功能和用法、主要函数的简单功能。最有效
的方法就是自己组织一个由简到繁的学习步骤,比如:在学习VB简单绘图功能时,学着自己绘制正弦曲线;学习使用picturecl控件来制作一个液晶效果显示时间;在学习API函数时,制作自己的任务栏图标。这种方法能够很好的帮助你掌握VB中常用对象、事件的使用。
最灵活的语言——Delphi
对于Delphi来讲,如果你曾经学过PASCAL的话,你会发现很多东西都是“似曾相识”的。确实如此,Delphi本身就是由和PASCAL结构类似的Object
Pascal编写而成。如果你有一定的PASCAL基础,学习起来当然更轻松一些。另外,还应注意对Delphi中包的理解和应用。比如,对运行期包、设计期包以及自己建立包应该注意的事项。在熟练掌握Object Pascal语言的基础上,可以把学习Delphi时的重点放到如何使用Delphi编写Windows应用程序,Delphi数据库编程和创建控件以及程序开发上面。
最强大的语言——Visual C++
VC++中由于有较完整的微软基本类库,使开发Windows应用程序变得简单而
高效。它提供的复杂的资源编辑器,可以很方便地编辑对话框、菜单、工具栏以及其他组成元素,因此它的功能强大,使用方便,但是和VB、Delphi相比,入门较难、不容易掌握。学习VC++,应该首先把重点方在基本数据类型、输入/输出、循环和数组、模板以及如何创建C++应用程序上面。在此之后,才应该进一步学习VC++中资源编辑器的使用、ActiveX控件的使用以及编辑器优化等内容。目前很多关于VC++的书籍中大部份都是对使用VC++各个版本开发应用程序的介绍,而忽略了C++的基本知识,实际上,对于初学者来讲,直接从具体应用程序设计入手而忽略基本知识,反而会在很大程度上加大学习难度,很容易丧失学习的信心?!---->
当然在上述这些语言中,都提供有自己的数据库编程和Internet编□'7b。在对程序本身熟练掌握的基础上,你还可根据自己的需要选择学习。当你已具有一定程序设计能力时,不妨自己设计一个现实、有兴趣的课题,自行开发、自行设计。这样可以帮你很好地熟练
二、数据库编程
如果你主要是想完成数据库方面的设计,那么应该选择专门的数据库开发工具,常见的有:SQLServer、PowerBulider等。假如只是需要完成相对简单的数据库功能,那就仔细看看Access吧。花不多的时间,也能让你有很大的收获,另外,还有一些专门用于大型数据库开发的工具如:DB2、Sysbase以及Oracle,这一类的软件一般都需要有较扎实的数据库基础知识和一定的软件开发能力。如果你是从头开始的话,建议不要一开始就去学习这一类软件。PB和SQLServer主要是用于完成数据处理和数据库的访问。由于SQLServer和操作系统兼容性较好以及在网络中的广泛应用,目前很多业余爱好者都选择学习它,在学习SQLServer时,应把重点放在SQL语言的组成、SQL的事务管理以及SQL的查询和优化上面。而PB则一直都是很多从事数人来讲,在学习数据库基本使用技巧的同时,还应注意到在PB中,对于需要访问Windows特殊功能的应用程序需要调用API函数,比如:控制鼠标、创建目录以及闪□'7b窗口等。
在学习数据库编程时,可以尝试着完成一个完整的软件开发周期。从需求分析隍7d始,亲身经历设计数据库、创建数据库前端、创建表格、完成常见查询以及输出报表等全部过程。
三、网络编程
目前较为流行的网络编程工具可谓“风格迥异,百花齐放”。每一种工具都有其独特的特点,在互联网实现上都有各自的优势。
在学习Java时,应该明确其中对象、继承等面向对象的基本理论,从根本上掌握Java的运行机制。在学习中应把重点放在其语言基础、Java与ActiveX以及JavaBeans等组件之间的编程技术以及应用上来。你可以自己编一些较为经典的小程序来加深基本技能的掌握:用Java的图形处理能力编写纸牌游戏,用AWT编写扫雷或者俄罗斯方块、用Java的网络功能编写网络聊天室等等。
相对来讲,ASP编辑方式简单、容易上手、功能强大,早已被广泛采用。在学
习ASP时,应该首先对ASP相关的网络知识、Web Server设置、HTML语言等方面有
一定了解,在此基础上,注重ASP的主要编程语言VBScript、ASP的常用对象和组
件的学习。如果想对ASP有较深入的了解的话,还必须对其常用的数据库
SQLServer、Acess等具有基本的使用技能。你可以自己尝试编一个简单的网上投票、网上超市等程序来加深对上述知识的掌握。
另外,值得一提的还有网络编程语言的“新秀”——PHP。它□'5c脱了ASP只能使用NT或者Windows 2000为服务器操作系统的限制,对于数据库的访问较ASP更快、更直接。但是目前对于初学者来讲,其语言比较难理解,安装相对复杂、可供参考和学习的资料远不及ASP。同时在Windows中使用PHP,并不能够很好地体现其优越性。因此,我们建议对于没有Linux/Uninx基础的初学者,最好不要从PHP学起。
网络编程还有一个很大的特点:处处留心皆学问。时常留意互联网上各个网站的设计,留心他人的设计思想和创意,会使你的网络编程技巧进步很快。总的来讲,网络编答7b本身难度并不大,但是由于其在具体项目设计上涉及范围较广,比如涉及到与各种各样的数据库直接的接口、 学习语言的过程,是自己创造、自己编程的过程,也是逐渐掌握、逐渐应用的过程。在学习过程中逐渐培养起良好的编程思维、经验,逐步提高程序设计能力、找到更好的设计方案才是编程开发的真正精髓和乐趣所在!
懂数据结构怕不行,我说的高手,不是在会不会的界限上,而是在能不能的界限上
(100分)策划 俄罗斯方块 完整的游戏过程 (可以的话还加50分,谢谢)
网上下载的CSDN里面的代码^^^
分享…………
我用TC调试成功了
因为头文件里有 所以不能在VC中运行
#include
#include
#include /*这里须要读取系统运行时间来作为定时器*/
#include /*很不幸,TC2的简单图形,让我放弃了用*/
#include /*win32+openGL来讲解.*/
#define MAX_X 14 /*可见最大X*/
#define MAX_Y 21 /*可见最大Y*/
/*我们定义了最大的可见X和Y,那么即还有不
可见的部分,事实上地图(大盒子)里的左右
两侧和底部各两行都被1填充,这样大大简化
出界的判断,事实上,在本例中没有这样的
代码,因为旁边有一圈1阻止小盒子越出大
盒子的按制范围
*/
#define MAX_C 7 /*最大种类,这个无须解释*/
#define KEY_UP 'w' /*定义上下左右按按键*/
#define KEY_DOWN 's'
#define KEY_LEFT 'a'
#define KEY_RIGHT 'd'
#define KEY_ESC 27 /*退出*/
typedef int BOOL;
#define FALSE 0
#define TRUE 1 /*这几个TC中没有...自己定义一下吧:)*/
/*时钟结构*/
typedef struct { /*时钟结构*/
BOOL enabled; /*时钟是否开启*/
unsigned int intervel; /*定时间隔*/
unsigned int lasttime; /*这个属于内部使用变量*/
} Timer;
/*
*现在进入了编程的初质阶段
*在开始处我会写出所有的函数原形,以及它们的作用
*main函数在程序的最后,你可以在这里看到整个游戏的组织架构
*很好,它只有几十行,并且非常容易理解,当然,还是先看一下函数原形
*及解释
*/
/******************************************************\
* 函数原形及说明 *
\******************************************************/
/*以下三个函数可以参照Timer结构体.在函数声明后面*/
int GetTickCount(); /*返回电脑或操作系统运行逝去的时间*/
/*在win32环境下已包含在windows.h里边,返回的是4byte*/
/*在DOS(本代码)环境下,要自己编写,使用到BIOS.h内容*/
int setTimer(Timer *t, unsigned int intv, BOOL en);
/*设置时钟t,参数分别为时钟指针,时间间隔,是否活动*/
/*时间间隔,win32下为毫秒,DOS下为1/18秒(有点低)*/
BOOL testTimer(Timer *t); /*测试时钟t是否到达定时时间*/
/*如下面这段代码:*/
/*
setTimer(&t, 1, 1); 设置1个单位的间隔
while(1) {
if(testTimer(&t)) printf("Active!\n");
}
将会定时(1个单位)在屏幕上打印Active!
一般来说testTimer必须放在循环中反复执行,激活时返回1
*/
void render(void); /*唯一的绘图函数*/
/*注意,此函数重画整个地图,根据地图中的点阵,以及根据
小盒在地图的中坐标在恰当位置画出小盒子*/
/*DOS的图形当然是很低的,但,全屏绘图在这里还是过得去
的,我用的是双缓冲,交换绘图,这样感觉好点*/
void initMap(void); /*初始化地图(大盒子)*/
/*之前提到过在这个两维数组中有一圈为1的东西来阻止
小盒子出界,这就是生成这一圈的函数*/
void newGame(); /*新建一个游戏*/
/*这个函数初始化一几个时钟和建造第一个下落的小盒子*/
/*当然建造完后要生成一个个的预览*/
void rotateBox(int box1[5][5], int box2[5][5]);
/*核心函数成员,把box1逆时针旋转90度,并保存到box2中*/
void rebuidNext();
/*核心函数成员,生成下一个方块*/
int drop();
/*核心函数成员,将下落的盒子向下移(实际上增加下落盒
子的Y值而已,当然要判断是否与地图点阵重叠*/
/*与地图重叠,无法完成下落操作,返回0*/
void putBox();
/*在这之上,下落的盒子与地图之前是独立的两个两维数*/
/*当下落失败后,小盒子要回到顶端再次重新执行下落,这*/
/*时原来的盒子内容当然就要变成地图上的内容了,putBox
就是将下落盒子的内容根据XY写到地图上*/
void clear();
/*这个函数在下落失败并putBox后执行,扫描整个地图*/
/*清除掉满行的点阵,具体细节在函数内讲*/
int move(int dir);
/*左右移动下落盒子,dir指出向左还是向右,这个与drop
是一样的*/
int test(int mx, int my, int box[5][5]);
/*这个比较重点,判断box在mx,my为坐标上,与地图上的
非空点阵是否有重叠.很通用的一个函数*/
int rotate();
/*旋转下落的盒子,当然如果转了之后与地图有冲突,会
取消转动,返回0,但返回的值好像没什么用~*/
int newfall();
/*创建下落元素,把"下一个"预览的内容复制到下落盒子*/
/*并将下落的盒子移动到地图顶部,当然这个过程,如果顶
部有冲突,会返回0,这时说明已经满了...gameOver*/
/******************************************************\
* 变量区 *
\******************************************************/
/*在上面的说明中,可能会有一些蒙,因为可能对所用到的实际变量没
*有了解
*/
int map[MAX_Y+4][MAX_X+4]; /*地图\大盒子...MAX_X,Y是可见面积*/
/*我已说过需要在外面布两圈"卫兵"*/
int curbox[5][5]; /*当前下落的盒子*/
int curx, cury; /*保存着当前活动盒子在地图上的位置*/
int nextbox[5][5]; /*保存着下一个形状的盒子*/
/*以上就是这么几个盒子和坐标了*/
/*这里列出了标准七种俄罗斯方块图形点阵,用到时它们会被复制到相*/
/*应的盒子...:)*/
int box[MAX_C][5][5] = { /*MAX_C(7)种预定义的盒子*/
{
{0,0,0,0,0},
{0,0,0,0,0},
{1,1,1,1,0},
{0,0,0,0,0},
{0,0,0,0,0}
},
{
{0,0,0,0,0},
{0,0,1,0,0},
{0,1,1,1,0},
{0,0,0,0,0},
{0,0,0,0,0}
},
{
{0,0,0,0,0},
{0,1,1,0,0},
{0,0,1,1,0},
{0,0,0,0,0},
{0,0,0,0,0}
},
{
{0,0,0,0,0},
{0,0,1,1,0},
{0,1,1,0,0},
{0,0,0,0,0},
{0,0,0,0,0}
},
{
{0,0,0,0,0},
{0,1,1,0,0},
{0,0,1,0,0},
{0,0,1,0,0},
{0,0,0,0,0}
},
{
{0,0,0,0,0},
{0,0,1,1,0},
{0,0,1,0,0},
{0,0,1,0,0},
{0,0,0,0,0}
},
{
{0,0,0,0,0},
{0,0,1,1,0},
{0,0,1,1,0},
{0,0,0,0,0},
{0,0,0,0,0}
}
};
/******************************************************\
* 时钟 *
\******************************************************/
/*时钟部分也非常理解的,一个用到设置时钟,一个用来测试时钟激活态*/
Timer tDown; /*正常下落定时时钟intervel会比较大*/
Timer tFast; /*按KEY_DOWN时使用的快速下落*/
int speed = 13; /*控制下落时间间隔*/
#define FAST_INTV 1 /*快时钟的间隔*/
/******************************************************\
* 时钟 *
\******************************************************/
/*时钟部分也非常理解的,一个用到设置时钟,一个用来测试时钟激活态*/
Timer tDown; /*正常下落定时时钟intervel会比较大*/
Timer tFast; /*按KEY_DOWN时使用的快速下落*/
#define FAST_INTV 1 /*快时钟的间隔*/
int GetTickCount() { /*读取BIOS时钟*/
int ret;
ret = peek(0x0,0x46e); /*实际上读取了内存0:046e处的内容*/
ret <<= 8; /*这个地方是$%#$^$%&^*/
ret += peek(0x0,0x46c); /*太多新的东西了,找点书看一看吧*/
return (ret);
}
int setTimer(Timer *t, unsigned int intv, BOOL en) {
t -> enabled = en; /*设置一个时钟罗*/
t -> intervel = intv;
t -> lasttime = GetTickCount(); /*lasttime记录的是上一个*/
/*tickcount返回的东西*/
/*这样当再一次测试时间时新的tickcount产生了
它来减去上一次的tickcount就得出了一个时间
间隔,这个就可以和intervel比较从而得出是否
激活了
*/
return 0;
}
BOOL testTimer(Timer *t) { /*在上面6行的地方解释了:)*/
unsigned int tmp, dt;
if (!(t -> enabled)) return FALSE;
tmp = GetTickCount();
dt = tmp - (t -> lasttime);
if(dt >= t -> intervel) {
t -> lasttime = tmp;
return TRUE;
}
return FALSE;
}
/******************************************************\
* 渲染部分 *
\******************************************************/
/*提供render更新整个屏幕*/
/*关于这个函数,要说的东西还是比较多,为了追求漂亮和编译*/
/*时的灵活性,这个函数被写得颇为冗长...*/
/*现在写一下本游戏图形的东西...使用TC2的Graphics我也不
太乐意,毕竟它本身已过时,但鉴于实在简单实用,它用来教学
再合适不过,这也是教学总是用TC的原因,老师们不喜欢让学
生问一些让他们掌握起来也困难的东西...*/
/*这里我使用了VGAMED模式,而不是 VGAHI,因为VGAMED有两个
页(可以想像成缓冲),这样可以用来做到不闪动画.即:在后台
页绘制图形,完成后再显示出来.
这里用到了两个函数:
setactivepage(1 | 0) 参数只能是1或0,选择绘图页,例如选
择了1后,以后所有的绘图动作将画到页1上.
setvisualpage(1 | 0) 这个叫做选择可见页,即选择在屏幕上
显示页面1还是0
*/
void render(void) {
int x, y;
static int cPage = 0; /*当前页,换页用*/
#define STARTX 50 /*定义几个常量*/
#define STARTY 0
#define LEN 18
setactivepage(cPage=(cPage == 0?1:0)); /*选择页*/
cleardevice(); /*清屏*/
setcolor(15);
rectangle( STARTX + LEN * 2 - 2,
STARTY + LEN * 3 - 2,
STARTX + LEN * (MAX_X - 2) + 2,
STARTY + LEN * (MAX_Y - 2) + 2);
/*用白色画一个外框*/
setfillstyle(SOLID_FILL, 5);
for(y = 3; y < MAX_Y - 2; y++) { /*画地图 */
for(x = 2; x < MAX_X - 2; x++) {
if(map[y][x]) {
rectangle( x * LEN + STARTX,
y * LEN + STARTY,
x * LEN + STARTX + LEN,
y * LEN + STARTY + LEN);
bar( x * LEN + STARTX + 1,
y * LEN + STARTY + 1,
x * LEN + STARTX + LEN - 2,
y * LEN + STARTY + LEN - 2);
}
}
}
/*绘图操作就不要作太复杂的介绍了,这只写作用*/
/*以上段,根据地图上的点阵情况将地图反映到屏幕上*/
for(y = 0; y < 5; y++) { /*画下落物*/
for(x = 0; x < 5; x++) {
if(curbox[y][x]) {
if(y + cury > 2) {
rectangle( (x + curx) * LEN + STARTX,
(y + cury) * LEN + STARTY,
(x + curx) * LEN + STARTX + LEN,
(y + cury) * LEN + STARTY + LEN);
bar( (x + curx) * LEN + STARTX + 1,
(y + cury) * LEN + STARTY + 1,
(x + curx) * LEN + STARTX + LEN - 2,
(y + cury) * LEN + STARTY + LEN - 2);
}
}
}
}
/*以上将下落的盒子按昭它在地图上的坐标,画到对应的区域里*/
for(y = 0; y < 5; y++) { /*画下一个*/
for(x = 0; x < 5; x++) {
if(nextbox[y][x]) {
rectangle( x * LEN + 320,
y * LEN + 10,
x * LEN + 338,
y * LEN + 28);
bar( x * LEN + 321,
y * LEN + 11,
x * LEN + 336,
y * LEN + 26);
}
}
}
/*这个画出下一个盒子的预览*/
setvisualpage(cPage); /*确认在cPage页里画好了*/
/*将它显示出来*/
}
/******************************************************\
* 初始化部分 *
\******************************************************/
/*提供newGame()初始化新游戏*/
void initMap(void) { /*初始化地图*/
int x, y; /*我们须要一圈卫兵...*/
for(y = 0; y < MAX_Y; y++) {
for(x = 0; x < MAX_X; x++) {
if(x MAX_X - 3 || y > MAX_Y - 3)
map[y][x] = 1;
else map[y][x] = 0;
}
} /*这里初始化出这个形状*/
} /*当然是无盖的...*/
void newGame() { /*新建游戏*/
int x, y;
initMap(); /*初始化地图*/
srand(GetTickCount()); /*初始化随机发生器*/
rebuidNext(); /*建立下一个*/
setTimer(&tDown, speed, 1); /*启动时钟(快慢两个)*/
setTimer(&tFast, FAST_INTV, 1);
newfall(); /*对下落的盒子操作一下*/
/*这样第一个下落的方块
就在地图顶部准备好了*/
}
/******************************************************\
* 核心函数 *
\******************************************************/
void rotateBox(int box1[5][5], int box2[5][5]) {
/*旋转box1输出到box2*/
int x, y;
for(x = 0; x < 5; x++) /*这个函数可以须要实际*/
for(y = 4; y >= 0; y--) /*编写一下才能印像深刻*/
box2[y][x] = box1[x][4 - y];
}
void rebuidNext() { /*新建下一个形状并放到nextbox中*/
int i, x, y;
i = random(MAX_C); /*从几种方块里面选一种*/
for(y = 0; y < 5; y++) /*并复制过来*/
for(x = 0; x < 5; x++)
nextbox[y][x] = box[i][y][x]; /*复制*/
}
int drop() { /*下落,返回成功与否*/
int newy; /*盒子要下落的新位置*/
newy = cury + 1; /*为当前Y位置+1*/
if(test(curx, newy, curbox)) {
cury = newy; /*测试下落盒在这个位置*/
return 1; /*上是否有冲突,没有的话*/
} /*直接设置cury*/
return 0;
}
void putBox() { /*将curbox填充到地图上*/
int x, y;
for(y = 0; y < 5; y++) /*这个也简单,主要是要根*/
for(x = 0; x < 5; x++) /*据curx,cury指出位置 */
if(curbox[y][x])
map[y + cury][x + curx] = curbox[y][x];
}
void clear() { /*清除掉满行*/
/*这个函数实际上效率也很低的,为了简便
它从头到尾作了测试*/
/*具体的算法为:
从第0行开始到最后一行,测试地图点阵是否为满,如果是的话
从当前行算起,之上的地图向下掉一行*/
int x, y;
int dx, dy;
int fullflag;
for(y = 0; y < MAX_Y - 2; y++) { /*最后两行保留行*/
fullflag = 1; /*假设为满*/
for(x = 2; x < MAX_X - 2; x++) { /*保留列~*/
if(!map[y][x]) {
fullflag = 0;
break;
}
}
if(fullflag) { /*向下移动一行*/
for(dy = y; dy > 0; dy--)
for(dx = 2; dx < MAX_X - 2; dx++)
map[dy][dx] = map[dy - 1][dx];
for(dx = 2; dx < MAX_X - 2; dx++)
map[0][dx] = 0;
/*并清除掉第一行*/
}
}
}
int move(int dir) { /*返回成功与否*/
int newx;
if(dir) newx = curx + 1;
/*与drop一样,准备移动后的坐标*/
else newx = curx - 1;
if(test(newx, cury, curbox)) { /*测试是否冲突*/
curx = newx; /*可以的话切换curx*/
return 1;
}
return 0;
}
int test(int mx, int my, int box[5][5]) {
/*测试box在map里mx,my位置上是否能着陆*/
/*这个是最关键的一个函数,它判断是否产生非空冲突*/
/*但算法还是很简单的*/
int x, y;
for(y = 0; y < 5; y++)
for(x = 0; x < 5; x++)
if(map[y + my][x + mx] && box[y][x])
return 0;
return 1;
}
int rotate() {
int x, y;
int newbox[5][5]; /*我们必须将当前盒子转动到新的盒子*/
/*再对这个新的盒子的冲突作测试*/
rotateBox(curbox, newbox); /*转动到新的盒子*/
if(test(curx, cury, newbox)) {
/*并且新的盒子能放到地图上而不冲突*/
for(y = 0; y < 5; y++)
for(x = 0; x < 5; x++)
curbox[y][x] = newbox[y][x]; /*复制进来*/
return 1;
}
else return 0;
}
int newfall() { /*创建下落元素失败返回0*/
int x, y;
curx = MAX_X / 2 - 2; /*重新指定小盒位置*/
cury = 0;
for(y = 0; y < 5; y++)
for(x = 0; x < 5; x++)
curbox[y][x] = nextbox[y][x];/*将nextBox复制过来*/
rebuidNext(); /*重建nextBox*/
return test(curx, cury, curbox);
}
/************************************************************\
* 主函数 -- 整个游戏架构 *
\************************************************************/
int main() {
char key; /*记录当前按键*/
int i;
int gd = VGA, gm = VGAMED; /*初始化的图形模式*/
Timer *ptDown; /*下落所指向的时钟(有快慢)*/
Timer trender; /*为了避免渲染给程序造成过大的负担*/
/*用一个时钟来控制渲染速度*/
/*把它设置interval = 1,*/
/*这样就是18 FPS了,当然无法达到标*/
/*准的60 FPS...毕竟这是DOS...*/
setTimer(&trender, 1, 1);
initgraph(&gd, &gm, ""); /*初始化图形*/
newGame(); /*新游戏...*/
while(1) { /*主游戏循环*/
if(kbhit()) { /*如果键盘有按下*/
key = getch(); /*读取一个按键值到key*/
}
else key = 0;
switch(key) { /*对读到的key进行判断*/
case KEY_UP:
rotate(); /*上,旋转下落盒子*/
break;
case KEY_DOWN:
ptDown = &tFast; /*使用tFast时钟 */
break;
case KEY_LEFT:
move(0); /*左移*/
break;
case KEY_RIGHT:
move(1); /*右移*/
break;
case KEY_ESC:
closegraph(); /*结束游戏*/
exit(0);
default:
ptDown = &tDown; /*使用原来速度 */
}
if(testTimer(ptDown)) { /*在上面已设置了下落要
使用的时钟在ptDown里*/
if(!drop()) { /*下落,失败返回0*/
putBox(); /*写到地图里*/
clear(); /*清除满行*/
if(!newfall()) { /*新建下落,失败则游戏结束*/
closegraph();
exit(0);
}
}
}
if(testTimer(&trender)) /*最后...渲染...*/
render();
}
}