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

article/2025/9/30 15:06:09

目录

一、效果展示

二、游戏说明

三、开发环境

四、代码展示

五、代码详解

1.游戏区域表示

2.方块表示

3.方块旋转

4.消行处理

 六、个性化定制

七、结语


一、效果展示

71130eff721e45a0982bee8130345bd8.gif

二、游戏说明

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

三、开发环境

  • 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。

f5f530e6474f4857b4501508a467bd29.png

//方块表示
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();

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

七、结语

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

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

 

 


http://chatgpt.dhexx.cn/article/xVaidkmk.shtml

相关文章

超详细!SFML库vs2022配置教程

1.官网下载SFML 官方网址&#xff1a;SFML 点击下载 根据自己电脑选择合适的版本下载 我的电脑是64位的 下载完成之后将文件放在一个自己方便找到的地方 2.vs2022配置 选择空项目 添加cpp源文件 附加包含目录 添加你SFML中的include路径 例如&#xff1a; 添加SFML中的li…

SFML学习笔记(1)——初识SFML

一、SFML SFML 是多媒体库&#xff0c;它为PC的各个组件提供简单的界面&#xff0c;用来简化游戏和多媒体应用程序的开发。 SFML分成以下五个模块&#xff0c;这些模块相互独立&#xff0c;也可以根据需要组合在一起: System(系统)&#xff1a;一个核心模块&#xff0c;它定义…

找出数组中第k大的数

给定一个数组&#xff0c;找出数组中第k大的数。其实现代码如下: package com.threeTop.www;/*** 找出数组中第k大的数* author wjgs**/ public class FindK {public static void find(int[]array,int begin,int end,int k){int ipartition(array,begin,end);if(i1>k){//左半…

n个数中找最大数c语言,N个数中找到第K大的数值(C语实现)

N个数中找到第K大的数值(C语实现) N个数中找到第K大的数值(C语实现) 研究生了,选了计算机算法这门课程,这周布置了一个作业,在OJ上做:**N个数中找到第K大的数值**。大一简单学过C语言基础,目前只能用C语言编程,后续会学C++编程。 分享一份不超时的C语代码~ 测试例子: 思…

快速排序计算第K大的数

先说结论&#xff0c;最终版本代码如下&#xff1a; public class KthNum {public static int k 2;public static boolean bigK false;public static void main(String[] args) {int arr[] {3, 2, 3, 1, 7, 4, 5, 5, 6};int kNum quickSort(arr);System.out.println("…

寻找第K大的数 C语言实现的一种方法

描述 在一个数组中&#xff0c;找到第K 大的数值一个数组&#xff0c;如&#xff1a;[3,2,1,5,6,4] &#xff0c;输入 2&#xff0c;返回&#xff1a;5也就是这个K的取值&#xff0c;是从 1 开始的&#xff0c;不超过数组的最大个数 解决思路 可以使用任意的排查函数&#x…

C++——寻找第k大的数

给出一个数组&#xff0c;找出数组的第k大的数&#xff1a;基于快速排序的思路&#xff0c;每次快排后&#xff0c;基准的左边都是比其小的数&#xff0c;右边都是比其大的数&#xff0c;一次快排结束后&#xff0c;若基准所处的位置正好是第k大&#xff08;即基准右边有k-1个数…

面试题:从n个数中找出第K大的数

参考https://blog.csdn.net/orangefly0214/article/details/84997668的思路 从有n个元素的乱序数组中找出第k大的元素 方法1&#xff1a;基于冒泡排序和简单选择排序&#xff0c;时间复杂度o(n*k) 我们知道&#xff0c;在冒泡排序和简单选择排序中&#xff0c;最外层的循环每…

在N个数中查找第K大的数字(Top K问题)

在N个乱序数字中查找第k大的数字&#xff0c;时间复杂度可以减小至 O(N*logN)O(N)O(1)O(2) 答案&#xff1a;B 所谓“第&#xff08;前&#xff09;k大数问题”指的是在长度为n(n>k)的乱序数组中S找出从大到小顺序的第&#xff08;前&#xff09;k个数的问题。 注意&…

idea控制台乱码解决方法

一、问题情况&#xff1a; IntelliJ IDEA 控制台输出中文乱码部分如图所示&#xff1a; 二、解决方法&#xff1a; 1.打开tomcat配置页面&#xff0c;Edit Configurations。 2.选择项目部署的tomcat&#xff0c;在配置项VM options文本框中输入-Dfile.encodingUTF-8,点击Appl…

idea 控制台乱码问题的问题

工程(环境window10&#xff0c;gradle-6.8.3版本&#xff0c;编辑器IntelliJ IDEA 2019.3.4&#xff09; 一&#xff09;问题表现编译build的时候控制台带中文信息的会乱码 1、 找到Help->Editor Custom VM Options 2、 添加 -Dfile.encodingUTF-8 该设置是针对编辑器级别…

IDEA中控制台乱码的解决方式

1.在设置中的“文件编码”中将3个位置设为UTF-8&#xff0c;注&#xff1a;此处设置与控制台乱码无关&#xff0c;3处可均设为UTF-8或均设为系统默认值。 2.在Tomcat的“编辑配置”中&#xff0c;将VM options设为-Dfile.encodingGBK(与第三步类似&#xff0c;默认即为GBK)。…

IDEA控制台乱码终极解决办法

【关于IDEA中文乱码的终极解决方法】 查了很多资料&#xff0c;大多数博主都是修改四个地方&#xff1a;&#xff08;前四步&#xff09;如果前四步还是不行&#xff0c;可以看五、六步。第一步&#xff1a;idea 安装目录下/bin/idea.exe.vmoptions 和/bin/idea64.exe.vmoption…

IDEA控制台乱码解决

解决办法&#xff1a; 打开Intellij的安装的bin目录&#xff08;D:\Program Files\JetBrains\IntelliJ IDEA 14.0\bin &#xff09;&#xff0c;找到上图的两个文件&#xff08;根据你的系统是32位或64位选择其中一个配置文件&#xff09;&#xff0c;在配置文件中添加&#xf…

多种方法帮你解决tomcat项目部署,idea控制台乱码问题

解决在使用Tomcat过程中idea控制台出现的乱码问题 以下将介绍几种方法&#xff08;都是小编亲测实用的方法&#xff09;&#xff0c;尝试并寻找适合自己的方法即可 由于我已经处理过了乱码问题&#xff0c;我就重新配置一下 &#xff08;我有效解决的方案是把-Dfile.encoding…

idea 控制台乱码

1.我原来的修改idea的控制台乱码是 在ideasettings--Editor--File encodings修改为UTF-8 2.tomcat 中修改的conf logging.properties配置文件 由UTF-8修改为GBK 以上是我之前的修改.最近有个项目这样配置之后在项目运行的时候.有个页面要获取一个json字段.但是代码中有乱码的…

IDEA控制台乱码问题,原因解决方式,解决不了算我输

IDEA 控制台乱码问题 文章目录 IDEA 控制台乱码问题为了节省大家时间, 直接展示下我的编码配置方案我的编码配置原则我的编码配置 另外说一下几个重要但是和乱码无关的配置乱码原因解决方式我为什么推荐控制台使用 GBK我的编码设置思想第一种解决方案的弊端end 附加技巧如何找出…

解决idea控制台乱码

控制台乱码如下&#xff1a; 解决方案一&#xff1a;修改当前 Web 项目 Tomcat Server 的虚拟机输出选项 上方导航栏“Run→Edit Configurations…”进入配置页面&#xff0c;修改当前 Web 项目 Tomcat Server 的虚拟机输出选项 VM options 添加 -Dfile.encodingUTF-8 。重启&…

IDEA控制台乱码(已解决)

先来说说我遇到的问题&#xff0c;用IDEA打开项目首先可以保证编辑器内不会乱码&#xff0c;启动Tomcat后控制台出现乱码   我在网上找了很多方式都没有解决&#xff0c;大多数的方式由以下几种&#xff1a; 进入File->Settings->Editor->File Encodings 将右侧的所…

详细解决tomcat乱码 IDEA控制台乱码

1、启动Tomcat时打印出一大堆看不懂的文字 如下图&#xff1a; 原因&#xff1a;产生乱码的根本原因就是编码和解码不一致 解决办法&#xff1a;将打开tomcat的安装目录conf下的logging.properties文件&#xff0c;将java.util.logging.ConsoleHandler.encoding UTF-8 修改为…