目录

一、效果展示

二、游戏说明

三、开发环境

四、代码展示

五、代码详解

1.游戏区域表示

2.方块表示

3.方块旋转

4.消行处理

六、个性化定制

七、结语


一、效果展示

二、游戏说明

相信大家都玩过俄罗斯方块,五种按键就能带来极高的可玩性。如果想要开发属于自己的俄罗斯方块,首先就要实现这五种按键的功能,其次就是实现消行和积分等必要功能。如果想要自己的游戏更精美,就可以往里面添加优美的音乐和丰富的音效。

三、开发环境

  • Visual Studio 2022
  • SFML 2.5.1

四、代码展示

#include<SFML/Graphics.hpp>
#include<SFML/Audio.hpp>
#include<time.h>
#include<Windows.h>using namespace std;
using namespace sf;const int rowCount = 24;//游戏区行数
const int columnCount = 12;//游戏区列数int table[rowCount][columnCount] = { 0 };//游戏区域表示//方块表示
int block[7][4] =
{ 1, 3, 5, 7,//I字型3, 5, 4, 6,//Z字型2, 4, 5, 7,//S字型2, 3, 5, 7,//L字型3, 5, 7, 6,//J字型3, 5, 4, 7,//T字型2, 3, 4, 5,//O字型
};//隐藏控制台
void hideWindow()
{HWND hwnd = GetForegroundWindow();if (hwnd){ShowWindow(hwnd, SW_HIDE);}
}int blockIndex;//方块类型
int nextIndex;//下一方块类型bool ifNextBlock = false;//下一方块生成判断//下降速度
const float normalSpeed = 0.5;
const float fastSpeed = 0.05;
float speed = normalSpeed;int score = 0;//游戏得分
char Score[20];//得分字符串//游戏方块坐标表示
struct Point
{int x;int y;
} currentBlock[4], backBlock[4], nextBlock[4];//生成方块
void newBlock(int t)
{blockIndex = t;int n = blockIndex - 1;int dx = rand() % 11;//随机偏移//坐标转换for (int i = 0; i < 4; i++){currentBlock[i].x = block[n][i] % 2 + dx;currentBlock[i].y = block[n][i] / 2;}
}//绘制方块
void drawBlocks(Sprite * spriteFk, RenderWindow * window)
{//已经落在底部的方块for (int i = 0; i < rowCount; i++){for (int j = 0; j < columnCount; j++){if (table[i][j] != 0){spriteFk->setTextureRect(IntRect((table[i][j] - 1) * 35, 0, 35, 35));spriteFk->setPosition(j * 35 , i * 35);spriteFk->move(37,60);//偏移量window->draw(*spriteFk);}}}//正在降落的方块for (int i = 0; i < 4; i++){spriteFk->setTextureRect(IntRect((blockIndex - 1) * 35, 0, 35, 35));spriteFk->setPosition(currentBlock[i].x * 35, currentBlock[i].y * 35);spriteFk->move(37,60);window->draw(*spriteFk);}
}//绘制下一方块
void drawNextBlock(Sprite * spriteFK, RenderWindow * window)
{for (int i = 0; i < 4; i++){spriteFK->setTextureRect(IntRect((nextIndex - 1) * 35, 0, 35, 35));spriteFK->setPosition(nextBlock[i].x * 35, nextBlock[i].y * 35);if (nextIndex == 1){spriteFK->move(520, 160);}else if (nextIndex == 2){spriteFK->move(540, 140);}else if (nextIndex == 3){spriteFK->move(540, 140);}else if (nextIndex == 4){spriteFK->move(540, 140);}else if (nextIndex == 5){spriteFK->move(540, 140);}else if (nextIndex == 6){spriteFK->move(540, 140);}else if (nextIndex == 7){spriteFK->move(540, 150);}window->draw(*spriteFK);}
}//检查方块合法性
bool check()
{for (int i = 0; i < 4; i++){if (currentBlock[i].x < 0||currentBlock[i].x >= columnCount||currentBlock[i].y >= rowCount||table[currentBlock[i].y][currentBlock[i].x])//位置存在方块{return false;}}return true;
}//检查游戏失败
bool gameFailed()
{for (int i = 0; i < columnCount; i++){if (table[3][i] != 0){return true;}}return false;
}void drop()
{//下降实现for (int i = 0; i < 4; i++){backBlock[i] = currentBlock[i];currentBlock[i].y += 1;}//生成下一方块if (ifNextBlock == false){ifNextBlock = true;//随机下一方块nextIndex = rand() % 7 + 1;int n = nextIndex - 1;for (int i = 0; i < 4; i++){nextBlock[i].x = block[n][i] % 2;nextBlock[i].y = block[n][i] / 2;}}if (check() == false){//方块固化for (int i = 0; i < 4; i++){table[backBlock[i].y][backBlock[i].x] = blockIndex;}ifNextBlock = false;newBlock(nextIndex);//生成新方块}
}void leftRightMove(int dx)
{//平移实现for (int i = 0; i < 4; i++){backBlock[i] = currentBlock[i];currentBlock[i].x += dx;}//检查方块合法性if (!check()){for (int i = 0; i < 4; i++){currentBlock[i].x = backBlock[i].x;}}
}void rotatoMove()
{//O型方块特殊处理if (blockIndex == 7){return;}//还原备份for (int i = 0; i < 4; i++){backBlock[i] = currentBlock[i];}Point p = currentBlock[1];//旋转中心//顺时针旋转实现for (int i = 0; i < 4; i++){Point tmp = currentBlock[i];currentBlock[i].x = -tmp.y + p.y + p.x;currentBlock[i].y = tmp.x - p.x + p.y;}if (!check()){for (int i = 0; i < 4; i++){currentBlock[i] = backBlock[i];}}
}void downBottom()
{//落底实现while (check() == true){for (int i = 0; i < 4; i++){backBlock[i] = currentBlock[i];//还原备份currentBlock[i].y++;}}for (int i = 0; i < 4; i++){currentBlock[i] = backBlock[i];}
}//按键处理
void keyEvent(RenderWindow * window)
{int dx = 0;//平移量Event e;//事件变量while (window->pollEvent(e))//从队列事件中取一个事件{//窗口关闭if (e.type == Event::Closed){window->close();}//按键点按事件if (e.type == Event::KeyPressed){switch (e.key.code){case Keyboard::Up:rotatoMove();break;case Keyboard::Left:dx = -1;break;case Keyboard::Right:dx = 1;break;case Keyboard::Space:downBottom();default:break;}}//按键长按事件if (Keyboard::isKeyPressed(Keyboard::Down)){speed = fastSpeed;//加速实现}//平移if (dx !=0){leftRightMove(dx);}}
}void clearLine()
{int k = rowCount - 1;for (int i = rowCount - 1; i > 0; i--){int count = 0;for (int j = 0; j < columnCount; j++){if (table[i][j]){count++;}table[k][j] = table[i][j];}if (count < columnCount){k--;}else{score += 12;//得分}}
}int main(void)
{hideWindow();//隐藏控制台srand(time(0));//生成随机种子//导入背景音乐Music music;music.openFromFile("SC/Bg.ogg");music.setLoop(true);//循环播放music.play();//创建窗口RenderWindow window(VideoMode(714,960),//窗口大小"Tetris");//窗口名称//创建背景Texture Bg;Bg.loadFromFile("SC/Bg.jpg");Sprite spriteBg(Bg);//创建方块Texture Fk;Fk.loadFromFile("SC/Fk.jpg");Sprite spriteFk(Fk);//创建下一方块Texture FK;FK.loadFromFile("SC/Fk.jpg");Sprite spriteFK(FK);//导入文字Font font;//字体变量font.loadFromFile("SC/text.ttf");Text text1, text2, text3, text4, text5, text6, text7, text8, text9, text10;//文字样式text1.setPosition(475,350);//文字位置text1.setString("Score");//文字内容text1.setFont(font);//文字字体text1.setCharacterSize(30);//文字大小text1.setStyle(sf::Text::Bold);//文字格式,加粗//显示得分数值text2.setFont(font);text2.setCharacterSize(30);text2.setStyle(sf::Text::Bold);text3.setPosition(475,430);text3.setString("UP:Rotato");text3.setFont(font);text3.setCharacterSize(23);text3.setStyle(sf::Text::Bold);text4.setPosition(475,470);text4.setString("Down:Speed Up");text4.setFont(font);text4.setCharacterSize(23);text4.setStyle(sf::Text::Bold);text5.setPosition(475,510);text5.setString("Left:Left Shift");text5.setFont(font);text5.setCharacterSize(23);text5.setStyle(sf::Text::Bold);text6.setPosition(475,550);text6.setString("Right:Right Shift");text6.setFont(font);text6.setCharacterSize(23);text6.setStyle(sf::Text::Bold);text7.setPosition(475,590);text7.setString("Space:Fall");text7.setFont(font);text7.setCharacterSize(23);text7.setStyle(sf::Text::Bold);text8.setPosition(510,110);text8.setString("Next Block");text8.setFont(font);text8.setCharacterSize(25);text8.setStyle(sf::Text::Bold);text9.setPosition(110,320);text9.setString("Game Over");text9.setFont(font);text9.setCharacterSize(45);text9.setStyle(sf::Text::Bold);text10.setPosition(155,380);text10.setString("Enter:Restar");text10.setFont(font);text10.setCharacterSize(28);text10.setStyle(sf::Text::Bold);newBlock(rand() % 7 + 1);//生成新方块Clock clock;//计时器float timer = 0;int i = 0;// 游戏循环while (window.isOpen())//游戏循环{float time = clock.getElapsedTime().asSeconds();//从计时器重启到当前的时间clock.restart();//重启计时器timer += time;//得分数值显示sprintf_s(Score, "%d", score);text2.setString(Score);text2.setPosition(590, 350);keyEvent(&window);//按键事件if (timer > speed)//下降判定{drop();timer = 0;//重置时间}clearLine();//消行speed = normalSpeed;//还原速度//渲染window.draw(spriteBg);window.draw(spriteFk);window.draw(spriteFK);window.draw(text1);window.draw(text2);window.draw(text3);window.draw(text4);window.draw(text5);window.draw(text6);window.draw(text7);window.draw(text8);//绘制drawBlocks(&spriteFk, &window);drawNextBlock(&spriteFK, &window);if (gameFailed() == false){window.display();//刷新并显示窗口}else{//暂停背景音乐music.setLoop(false);music.play();//渲染window.draw(text9);window.draw(text10);//显示失败界面if (i == 0){window.display();}i = 1;Event r;//重新游戏while (window.pollEvent(r)){if (r.type == Event::KeyPressed){switch (r.key.code){case Keyboard::Enter://清屏for (int i = 0; i < rowCount; i++){for (int j = 0; j < columnCount; j++){table[i][j] = 0;}}score = 0;//重置得分i = 0;//重置window.display();break;default:break;}}}}}system("pause");return 0;
}

五、代码详解

1.游戏区域表示

游戏区域使用二维数组表示。如果数组中某位置的值不为0,表示该位置存在方块。

const int rowCount = 24;//游戏区行数
const int columnCount = 12;//游戏区列数int table[rowCount][columnCount] = { 0 };//游戏区域表示

2.方块表示

在2*4的矩阵中,可以表示俄罗斯方块中任何类型的方块。所以,方块的表示可以使用编号进行,如:I型方块表示为1,3,5,7。

//方块表示
int block[7][4] =
{ 1, 3, 5, 7,//I字型3, 5, 4, 6,//Z字型2, 4, 5, 7,//S字型2, 3, 5, 7,//L字型3, 5, 7, 6,//J字型3, 5, 4, 7,//T字型2, 3, 4, 5,//O字型
};

每种类型的方块都由四个小方块排列组合而成,所以可以使用结构体记录四个小方块的坐标,在表示游戏区域的二维数组内定位方块位置。

//游戏方块坐标表示
struct Point
{int x;int y;
} currentBlock[4], backBlock[4], nextBlock[4];

因为游戏的进行是以方块坐标的形式表示在游戏区域内,所以需要将表示方块的数组转换为方块的坐标。如:I型方块在游戏区域内应表示为(1,0),(1,1),(1,2),(1,3)。通过观察两组数据,可以发现两组数据之间的规律,即,横坐标等于方块数组除以2的余数,纵坐标等于方块数组除以2的商。据此,可进行坐标的转换。

//坐标转换
for (int i = 0; i < 4; i++)
{currentBlock[i].x = block[n][i] % 2;currentBlock[i].y = block[n][i] / 2;
}

3.方块旋转

方块旋转,简单来说,就是,在平面内,一个点绕着另一个点旋转90°。至于怎么求得旋转后的坐标,并不需要进行过多思考,只需懂得搜索引擎的使用,就可找到与之匹配的公式。在开发中,善于利用工具搭建自己的程序,也是一种重要的能力。

//顺时针旋转实现
for (int i = 0; i < 4; i++)
{Point tmp = currentBlock[i];currentBlock[i].x = -tmp.y + p.y + p.x;currentBlock[i].y = tmp.x - p.x + p.y;
}

4.消行处理

消行的原理为,从下至上,逐行检测,该行数组是否存在0,如该行数组都不为0,则该行数组被上一行数组替换。

void clearLine()
{int k = rowCount - 1;for (int i = rowCount - 1; i > 0; i--){int count = 0;for (int j = 0; j < columnCount; j++){if (table[i][j]){count++;}table[k][j] = table[i][j];}if (count < columnCount){k--;}
}

六、个性化定制

一个与众不同的背景,更能凸显这是个专属定制的俄罗斯方块。

//创建背景
Texture Bg;
Bg.loadFromFile("SC/Bg.jpg");
Sprite spriteBg(Bg);

方块的贴图,也是个性化的一方面。

//创建方块
Texture Fk;
Fk.loadFromFile("SC/Fk.jpg");
Sprite spriteFk(Fk);

一首悠悠动听的背景音乐,更能激发游戏兴致。

//导入背景音乐
Music music;
music.openFromFile("SC/Bg.ogg");
music.setLoop(true);//循环播放
music.play();

除此之外,还有许多个性化定制等待大家的发掘。

七、结语

希望博客们都能创造属于自己的俄罗斯方块!

如有错误之处,欢迎指正。

使用SFML框架打造属于自己的俄罗斯方块相关推荐

  1. Android进阶:框架打造之IOC框架

    什么是IOC IOC(Inversion of Control):控制反转.开发过程中类里面需要用到很多个成员变量 传统的写法:你要用这些成员变量的时候,那么你就new出来用 IOC的写法:你要用这些 ...

  2. 以太坊DApp开发入门教程——Node.js和truffle框架打造区块链投票系统

    第一节 课程概述 本课程面向初学者,内容涵盖以太坊开发相关的基本概念,并将手把手地教大家如何构建一个 基于以太坊的完整去中心化应用 -- 区块链投票系统. 通过本课程的学习,你将掌握: 以太坊区块链的 ...

  3. 自己做量化交易软件(16)用小白通通量化AI框架打造自己的量化平台

    最近一段时间,我主要学习python3和tkinter的窗口开发,对tkinter编程逐步了解. 此外,应广大朋友要求,我写了 一本学习python3学习书籍<小白学Python3实战搭建量化投 ...

  4. 抢红包算法 c++_从0开始打造UI框架:动态化框架Scrollview物理学算法解析_搜狐汽车...

    | 导语 动态化是APP未来的趋势,腾讯成立了动态化框架中台,打造腾讯自研的动态化框架解决方案.ScrollView是动态化框架UI组件的核心之一,而物理学算法可能是其中最重要的部分之一了,好的物理学 ...

  5. 刚刚,旷视开源深度学习框架「天元」:Brain++内核,研发和落地都在用;孙剑:COCO三连冠背后的秘密武器...

    乾明 发自 凹非寺  量子位 报道 | 公众号 QbitAI 耗费资源打造优化6年,迭代到8.0版本,旷视技术体系的关键支柱,现在正式对外开源. 刚刚,深度学习驱动之下最早创业的中国AI独角兽旷视,宣 ...

  6. DockOne微信分享( 九十一):打造百亿级数据处理量的弹性调度容器平台

    本文讲的是DockOne微信分享( 九十一):打造百亿级数据处理量的弹性调度容器平台[编者的话]本次分享介绍七牛数据处理团队的容器技术实践经验,分享七牛如何通过自主研发的容器调度框架打造易扩展.易部署 ...

  7. THINKPHP框架的优秀开源系统推荐

    THINKPHP框架的优秀开源系统推荐 众所周知,国内众多优秀的开源框架,ThinkPHP从诞生以来一直秉承简洁实用的设计原则,在保持出色的性能和至简的代码的同时,也注重易用性.并且拥有众多的原创功能 ...

  8. three.js根据高德地图的经纬度信息打造3D地图

    代码效果: 依据的数据: 高德地图 根据地图的经纬度,利用three.js框架打造3D地图,同时可以把飞机.高铁.汽车等元素加入其中,给他们指定的路线,让他们动起来. 源代码地址:three.js打造 ...

  9. 【ASP.NET程序员福利】打造一款人见人爱的ORM(一)

          "很多人都不太认可以第三方ORM,因为考虑的点不够全面,没有大用户群体的ORM有保证,这点是不可否认确是事实.但是往往用户群体大的ORM又有不足之处,今天我们就来聊聊关于ORM的 ...

最新文章

  1. .sql文件如何执行_深入理解SQL原理:SQL查询语句是如何执行的?
  2. 寒假训练,2.25,J-Palindrome Names (回文
  3. .NET chart 毫秒级坐标轴
  4. linux shell ascii 字符 转换
  5. delphi tdxdbgrid 导出 excel_9个免费的Excel图表工具,1键即可做出高大上图表,月薪3万没问题...
  6. 操作主机 Infrastructure Master[为企业维护windows server 2008系列八]
  7. ie不支持max-height的解决之法
  8. CF思维联系– Codeforces-989C C. A Mist of Florescence
  9. hamcrest_重新设计Hamcrest
  10. 实用必备xp框架模块_Xposed框架安装、使用以及插件开发
  11. 弱小无助!苹果或将出手救援屏幕供应商JDI
  12. 在centos上安装html,Centos-7安装pdf2htmlEX
  13. 【前端成长-读书群】
  14. c语言编程 通讯录排序,C语言实现一个通讯录
  15. CocosCreator编辑器界面
  16. cad图纸怎么看懂_教你如何快速看懂建筑施工图纸
  17. 3月15日 | 开启 ICLR 2023预讲会专场二
  18. 男人要象狗 女人要象猫
  19. 关于向量值函数方程变分的一点注记
  20. IOG交互软件---Intelligent Pixel Annotation Tool (IPAT)---安装说明

热门文章

  1. maven 无法引入net.sf.json的解决方法
  2. QingTing.Fm-WPF是调用蜻蜓FMAPI 查询API内容展示,进行播放
  3. pdk开发学习之路-CDF参数(1)
  4. WinNas之seafile server私人云盘搭建
  5. 泛函分析 03.05 内积空间与Hilbert空间 - 可分的Hilbert空间
  6. python cv.calcHist()
  7. rabbitmq将默认root用户切换为其他用户
  8. java 酒店系统设计_基于Java的酒店管理系统的设计与实现
  9. volo拉丁语_【几句拉丁语名言】
  10. 国际大学生程序设计竞赛(ACM-ICPC)(大赛系列第14期)