/*……接上面的程序代码,受帖子内容限制请继续看下帖:*/
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 main();
/*终于到了最后的主函数,在这里可以看到整个游戏的架*/
/*构,包括游戏主循环,键盘处理等...*/
/******************************************************\
* 变量区 *
\******************************************************/
/*在上面的说明中,可能会有一些蒙,因为可能对所用到的实际变量没有了解*/
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 /*快时钟的间隔*/
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;
}
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 < 2 || 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();
}
}。
/* 本程序写完*/
我在Turbo C 中运行是它总说我语法错误。可是我就是不知道错在哪。请帮忙运行一下,帮我看看哪错了,本人不甚感激!!!!谢谢!
温馨提示:内容为网友见解,仅供参考