「C/C++」源码教程-手写经典俄罗斯方块

admin 2025-02-22 129人围观 ,发现78个评论
小白手写游戏经典-俄罗斯方块

儿时经典的掌机游戏,你还记得吗?

花式消行技巧,荣耀伙伴的最高纪录,仿佛又回到了纯真的战斗时代。

致敬俄罗斯方块之父"阿列克谢·帕基特诺夫",一起实现这个传奇经典游戏吧!

当时他耗费了6天时间,今天的你需要几天呢?

先看一下游戏效果:

搭建环境

首先,我们来搭建开发环境:

1.VC++2010或以上版本,推荐使用VS2015以上版本。

2.下载SFML库,可以到SFML官网下载,或者联系作者。

SFML是一个非常方便的工具库,我下载的是位版本(32位可以在32位和64位平台运行)

下载后解压到电脑的任意位置,比如:C:\SFML\_32bit

使用VC++或者VS创建一个空项目,然后配置头文件以及库文件。

配置使用的库文件:

准备游戏素材

准备好游戏的素材(图片,背景音乐,音效音乐,字体文件)。

背景图片如下,也可以更换成自己喜欢的其他图片。

游戏区域四周的方框图片,这个图片可以让游戏界面更好看哦,也可以省略不要。

俄罗斯方块的纹理图片如下。在代码中,我们会按照小方块的大小进行切割,得到多个不同颜色的小方块。

游戏逻辑结构

搭建游戏框架

先写好游戏框架:

includeSFML/#;voidkeyEvent(RerWindow*window){Evente;//pollEvent从事件队列中取出一个事件while(window-pollEvent(e)){if(==Event::Closed)window-close();if(==Event::KeyPressed){switch(){caseKeyboard::Up:break;caseKeyboard::Left:break;caseKeyboard::Right:break;default:break;}}}}intmain(){srand(time(0));//创建窗口RerWindowwindow(VideoMode(320,416),//窗口大小"Rock");//标题//创建表示图片的精灵Texturet2,t3;("images/");("images/");SpritespriteBg(t2);SpritespriteFrame(t3);while(()){keyEvent(window);(Color::White);(spriteBg);(spriteFrame);();}return0;}

运行后,效果如下:

俄罗斯方块的设计

俄罗斯方块的实现,有很多实现方式,最简单的方式是使用多个二位数组,每个二位数组来表示一种方块。不过有更高效的实现方式,使用一个二维数组来表示多种俄罗斯方块。

constintROW_COUNT=20;constintCOL_COUNT=10;intblocks[7][4]={1,3,5,7,//I2,4,5,7,//Z1型3,5,4,6,//Z2型3,5,4,7,//T2,3,5,7,//L3,5,7,6,//J2,3,4,5,//田};
把方块信息转换成位置坐标

所以对于"I"字型的方块,{1,3,5,7}就使用4个坐标来表示,(1,0),(1,1),(1,2),(1,3)

structPoint{intx,y;}curBlock[4],BakBlock[4];for(inti=0;i4;i++){curBlock[i].x=blocks[n][i]%2;curBlock[i].y=blocks[n][i]/2;}
俄罗斯方块的旋转

旋转效果,最简单的方式是,为每一种方向使用一个二位数组。我们使用最灵活的方式,对俄罗斯方块进行旋转处理。

直接使用通用的数学公式,就可以直接得到旋转后的坐标位置。

直接使用数学公式,平面中,一个点(x,y)绕任意点(dx,dy)顺时针旋转a度后的坐标

xx=(x-dx)*cos(-a)-(y-dy)*sin(-a)+dx;

yy=(x-dx)*sin(-a)+(y-dy)*cos(-a)+dy;

平面中,一个点(x,y)绕任意点(dx,dy)逆时针旋转a度后的坐标

xx=(x-dx)*cos(a)-(y-dy)*sin(a)+dx;

yy=(x-dx)*sin(a)+(y-dy)*cos(a)+dy;

我们需要的是逆时针旋转90度代入公式,得:

Pointp=curBlock[1];//centerofrotationfor(inti=0;i4;i++){structPointtmp=curBlock[i];curBlock[i].x=+;curBlock[i].y=+;}
使用时钟控制自由下落

每次循环开始的时候,就累加时间,如果时间超过延时间隔,就调用自定义的降落函数drop()

while(()){floattime=().asSeconds();();//计时器重启及时timer+=time;keyEvent(window);if(timerdelay){drop();//下降一个位置timer=0;}..}
游戏方块的渲染
voiddrawBlocks(RerWindow*window,Sprite*spriteBlock){//绘制已降落完毕的方块for(inti=0;iROW_COUNT;i++)for(intj=0;jCOL_COUNT;j++){if(table[i][j]==0)continue;spriteBlock-setTextureRect(IntRect(table[i][j]*18,0,18,18));spriteBlock-setPosition(j*18,i*18);spriteBlock-move(28,31);//offsetwindow-draw(*spriteBlock);}//绘制当前方块for(inti=0;i4;i++){spriteBlock-setTextureRect(IntRect(blockIndex*18,0,18,18));spriteBlock-setPosition(curBlock[i].x*18,curBlock[i].y*18);spriteBlock-move(28,31);//offsetwindow-draw(*spriteBlock);}}
消行的实现

从底部向上逐行扫描。

voidclearLine(){intk=ROW_COUNT-1;for(inti=ROW_COUNT-1;i0;i--){intcount=0;for(intj=0;jCOL_COUNT;j++){if(table[i][j])count++;table[k][j]=table[i][j];}//如果没有满行,就继续扫描上一行//如果已经满行,k不变,在这个满行内,存放下一次的扫描结果if(countCOL_COUNT)k--;else{score+=10;();}}chartmp[16];sprintf_s(tmp,"%d",score);(tmp);}
撸码捷径

越是优化代码,越是演练,越是思考,就越能发现C/C++的优势所在。

猜你喜欢
    不容错过