上节的v5版本使用的简单消行法并不符合传统的游戏规则,所以本节就使用传统的算法来做。虽然这个算法还有些小BUG,这些BUG将在后面的版本中进行修复。 下载地址:htt...
上节的v5版本使用的简单消行法并不符合传统的游戏规则,所以本节就使用传统的算法来做。虽然这个算法还有些小BUG,这些BUG将在后面的版本中进行修复。
下载地址:http://pan.baidu.com/share/link?shareid=396056&uk=940392313 (百度共享链接地址),进入zengl俄罗斯方块共享文件夹,在该文件夹中有个version_6_tetris.rar的压缩包就是v6版本的代码包,里面有个版本6说明.txt文件,包含了该版本的相关说明:
该版本是俄罗斯方块的第六个测试版本,采用传统的方式来消除装满方块的行,当某行装满方块时,先将该行消除,再将该行上方的方块下移,以填补消除的空隙, 不过该版本的消行算法还存在BUG,没考虑到夹在消除行中间的行,当要消除的行不是连续的时候,中间的行就不会下移,上面的行的下移距离也会不正确,从而 出现问题,该BUG将在后面的版本中进行修复。另外从该版本开始,添加了计分功能,每消除一行就增加10分(因为目前一行默认有10个小方块,如果一行设 为12个小方块,那么就会增加12分),得分将显示在主游戏区域的右上方,当一轮结束时,会显示game over的字符串信息,可以按R键重开一轮,按ESC键可以随时退出游戏。如果要玩完整版,可以查看"zengl编程语言"栏目,其中的"zengl编程语言v1.0.5 编译,执行,源码调试一体化,开发俄罗斯方块"这篇文章里有俄罗斯方块正式发布版的下载地址。
如果是linux用户,在成功编译了zengl v1.0.5或更高版本的程序后(注意编译需要root权限,因为需要将.so动态链接库拷贝到/usr/lib中),可以将 version_6_tetris.rar里的version_6_tetris文件夹拷贝到zengl根目录中,最后运行./zenglrun version_6_tetris/tetris.zl -n 即可,如果要调试脚本,可以加个-d参数(参数的含义请参考上面提到的zengl v1.0.5的文章,或者通过-h参数来查看帮助)。
修改过后的myRemoveMainBgFixTetris自定义函数如下:
/*
使用传统的方式,消除所有装满小方块的行,再将其上方的元素下移。
不过本版本的该算法,有个小BUG,就是当要消除的行不是连续的时候,中间的行就不会下移,上面的行的下移距离也会不正确,
从而出现问题,该BUG将在后面的版本中进行修复。
*/
fun myRemoveMainBgFixTetris()
global gameMainBg,scores_text;
clsGameMainBg gameMainBg; clsFont scores_text;
bottom = GameMainWidthNum * (GameMainHeightNum-1);
for(vy = GameMainHeightNum-1 ; vy >= 0 ; vy--) //循环消除所有满行的元素
if(gameMainBg.line_count_array[vy] == GameMainWidthNum)
end = (vy+1) * GameMainWidthNum;
for(index = vy * GameMainWidthNum ; index < end ; index++) //将这些行里的元素占用位设为0,即消除这些元素。
gameMainBg.fix_tetris[index].occupy = 0;
endfor
gameMainBg.line_count_array[vy] = 0; //将该行所拥有的方块数设为0
topvy = vy;
endif
endfor
for(i=0;i<GameMainWidthNum;i++) //循环将每列的元素向下移,以填补前面消除的行空隙。
for(index = i + bottom , vy = GameMainHeightNum-1 , needRemoveNum = gameMainBg.needRemoveNum;
vy >= 0 ; index-=GameMainWidthNum,vy--)
if(vy < topvy && gameMainBg.fix_tetris[index].occupy) //只有在topvy以上的元素会发生下移,所以该版本没考虑到夹在消除行中间的方块。
index2 = index + GameMainWidthNum * needRemoveNum; //得到按needRemoveNum下移后的方格索引
gameMainBg.fix_tetris[index2].startindex = gameMainBg.fix_tetris[index].startindex; //将原index处的形状样式赋值给index2处
gameMainBg.fix_tetris[index].occupy = 0; //再将原index处的方格设为非占用状态,即该方格内没有小方块。
gameMainBg.fix_tetris[index2].occupy = 1; //将下移后的index2索引处的方格设置占用状态。
gameMainBg.line_count_array[vy]--; //将原行方块数减一
gameMainBg.line_count_array[vy+needRemoveNum]++; //将新行方块数加一
endif
endfor
endfor
scores_text.scores += gameMainBg.needRemoveNum * GameMainWidthNum; //根据消除的行数和每行的方块数计算得分
sdlDrawTextEx(&scores_text.img , scores_text.fontobj, "scores:"+scores_text.scores , scores_text.fontcolor); //设置新的得分字符串的sdl位图表面
scores_text.needDraw = TRUE; //将得分结构的needDraw成员设为TRUE表示需要重新绘制得分信息。
gameMainBg.needRemoveNum = 0; //将需要消除的行数重置为0
endfun
本节v6版本的tetris.zl脚本中因为需要将得分和游戏结束时的字符串信息绘制出来,所以需要用到SDL的第三方字体库,下面看下新用到的脚本函数:
1) sdlFontInit(); 对SDL字体资源进行全局初始化,在调用所有其他的字体相关的函数(例如绘制字符串函数等)前,必须使用sdlFontInit函数进行初始化。当然只需要初始化一次即可。
例如在该版本的tetris.zl脚本的第275行:
sdlFontInit(); //初始化字体库
2) sdlSetCaption(caption); 设置游戏窗口的标题,参数caption是要显示的标题字符串。
例如tetris.zl脚本的第282行:
sdlSetCaption('俄罗斯方块 powered by zengl copyright:zengl.com author:zenglong'); //设置游戏窗口标题
3) sdlOpenFont(font_filename , font_size); 打开font_filename名字或相对路径对应的字体文件,并通过font_size来设置字体大小,返回字体资源的指针。
例如tetris.zl脚本的第281行:
scores_text.fontobj = turn_over_text.fontobj = sdlOpenFont('CANDARA.TTF',25); //加载字体文件
4) sdlDrawTextEx(ref surface,font obj , string , array fontcolor , array bgcolor[option]); option表示是可选参数,sdlDrawTextEx 和 以前的sdlDrawText区别在于,sdlDrawTextEx可以自动释放第一个参数的原表面指针,而sdlDrawText返回的表面指针在不用时,需要手动释放掉。第一个参数ref surface必须是整数类型的变量引用,且引用的变量值必须是有效的SDL表面指针,如果不是有效的指针,则虚拟机会输出错误信息,并终止脚本的执行。font obj参数为前面通过sdlOpenFont函数加载的字体指针。string参数为要显示的字符串信息, fontcolor数组或类为字符串要显示的前景色,bgcolor为可选参数,表示要显示的背景色。该函数返回含有字符串信息的位图表面指针。以后只要通过sdlBlitImg将该位图绘制到游戏窗口中,即可显示字符串信息。
例如tetris.zl脚本的第286和287行:
sdlDrawTextEx(&scores_text.img , scores_text.fontobj, "scores:"+scores_text.scores , scores_text.fontcolor); //生成得分信息字符串的位图表面
sdlDrawTextEx(&turn_over_text.img , turn_over_text.fontobj, "game over press 'R' key to restart!" , turn_over_text.fontcolor,array(0,0,0)); //生成游戏结束的字符串的位图表面
在tetris_def.zl脚本中添加了clsFont和字体有关的类:
class clsFont
fontobj; //SDL FONT加载的字体对象指针
fontcolor; //字体的前景色
img; //存放需要绘制的SDL字符串位图表面指针,字符串都是先转为位图,然后再绘制到游戏窗口的
scores; //得分
needDraw; //是否需要绘制
clsPosition pos; //字符串位图需要绘制到游戏窗口中的坐标信息
endcls
另外,在tetris.zl中添加了myReStartGame自定义函数来处理R键:
/*
当某一轮结束时,可以按"r"键,重新开始一轮,该函数将清除主游戏区域的所有方块,并重置得分等信息。
*/
fun myReStartGame()
global gameMainBg,tetris,turn_over,scores_text,screen;
clsGameMainBg gameMainBg; clsTetris tetris; clsFont scores_text;
for(i=0;i<gameMainBg.count;i++) //循环清除所有方块。
gameMainBg.fix_tetris[i].occupy = 0;
endfor
for(vy = GameMainHeightNum-1 ; vy >= 0 ; vy--)
gameMainBg.line_count_array[vy] = 0;
endfor
gameMainBg.needRemoveNum = 0;
myGetRandomIndex();
tetris.state = bltRandom()%TetrisStateNum;
tetris.xy.vx = bltRandom()%(GameMainWidthNum - 3);
tetris.xy.vy = 0;
scores_text.scores = 0;
turn_over = FALSE;
isneedDraw = TRUE;
scores_text.needDraw = TRUE;
time = sdlGetTicks();
sdlDrawTextEx(&scores_text.img , scores_text.fontobj, "scores:"+scores_text.scores , scores_text.fontcolor);
sdlFillRect(screen,NULL,array(0, 0xFF, 0xFF)); //将主屏幕清空。
endfun
至于脚本中的其他代码都在重要的部分加了注释,可以结合注释和调试器进行分析。该版本的脚本运行截图如下:
上图中,最上方的两个方块因为到了顶部所以重合了,该问题将在后面的版本中进行处理。
OK,下节介绍v7的版本,休息,休息一下 O(∩_∩)O~