原始代码呈现:

#include <SFML/Graphics.hpp>
#include <time.h>
using namespace sf;const int M = 20;
const int N = 10;int field[M][N] = {0};struct Point
{int x,y;} a[4], b[4];int figures[7][4] =
{1,3,5,7, // I2,4,5,7, // Z3,5,4,6, // S3,5,4,7, // T2,3,5,7, // L3,5,7,6, // J2,3,4,5, // O
};bool check()
{for (int i=0;i<4;i++)if (a[i].x<0 || a[i].x>=N || a[i].y>=M) return 0;else if (field[a[i].y][a[i].x]) return 0;return 1;
};int main()
{srand(time(0));     RenderWindow window(VideoMode(320, 480), "The Game!");Texture t1,t2,t3;t1.loadFromFile("images/tiles.png");t2.loadFromFile("images/background.png");t3.loadFromFile("images/frame.png");Sprite s(t1), background(t2), frame(t3);int dx=0; bool rotate=0; int colorNum=1;float timer=0,delay=0.3; Clock clock;while (window.isOpen()){float time = clock.getElapsedTime().asSeconds();clock.restart();timer+=time;Event e;while (window.pollEvent(e)){if (e.type == Event::Closed)window.close();if (e.type == Event::KeyPressed)if (e.key.code==Keyboard::Up) rotate=true;else if (e.key.code==Keyboard::Left) dx=-1;else if (e.key.code==Keyboard::Right) dx=1;}if (Keyboard::isKeyPressed(Keyboard::Down)) delay=0.05;<- Move -> ///for (int i=0;i<4;i++)  { b[i]=a[i]; a[i].x+=dx; }if (!check()) for (int i=0;i<4;i++) a[i]=b[i];//Rotate//if (rotate){Point p = a[1]; //center of rotationfor (int i=0;i<4;i++){int x = a[i].y-p.y;int y = a[i].x-p.x;a[i].x = p.x - x;a[i].y = p.y + y;}if (!check()) for (int i=0;i<4;i++) a[i]=b[i];}///Tick//if (timer>delay){for (int i=0;i<4;i++) { b[i]=a[i]; a[i].y+=1; }if (!check()){for (int i=0;i<4;i++) field[b[i].y][b[i].x]=colorNum;colorNum=1+rand()%7;int n=rand()%7;for (int i=0;i<4;i++){a[i].x = figures[n][i] % 2;a[i].y = figures[n][i] / 2;}}timer=0;}///check lines//int k=M-1;for (int i=M-1;i>0;i--){int count=0;for (int j=0;j<N;j++){if (field[i][j]) count++;field[k][j]=field[i][j];}if (count<N) k--;}dx=0; rotate=0; delay=0.3;/draw//window.clear(Color::White);    window.draw(background);for (int i=0;i<M;i++)for (int j=0;j<N;j++){if (field[i][j]==0) continue;s.setTextureRect(IntRect(field[i][j]*18,0,18,18));s.setPosition(j*18,i*18);s.move(28,31); //offsetwindow.draw(s);}for (int i=0;i<4;i++){s.setTextureRect(IntRect(colorNum*18,0,18,18));s.setPosition(a[i].x*18,a[i].y*18);s.move(28,31); //offsetwindow.draw(s);}window.draw(frame);window.display();}return 0;
}

简单分析:

1. 首先是各种初始化变量的含义

const int M = 20;//行数
const int N = 10;//列数//游戏共有20行,每行10个格子
//随着游戏进行,这些格子里面可能有方块,可能没有
//0表示当前格子没有方块
//除0以外其它的数字表示这里应该有方格,且方格的样子是通过这个数字计算出来的。
int field[M][N] = { 0 };//初始化
//数组 a[i]是方块经过一次变化后的新状态(例如经过平移,下落,旋转之后的状态)
//b[i]是a[i]变化前的状态,相当于是a[i]的备份。
//一旦经过变化的a[i]是不允许的形态,则要借助b[i]恢复到变化前的样子
Vector2i a[4], b[4]; int figures[7][4] = //方块的初始形状
{1,3,5,7, // I2,4,5,7, // Z3,5,4,6, // S3,5,4,7, // T2,3,5,7, // L3,5,7,6, // J2,3,4,5, // O
};

2.check方法解读:

/*检测a[i]记录的方块形态是否可行0:表示不可行 有四种情况是不可行的:i.    横向超出了左边界,ii.   横向超出了右边界,iii.  纵向越过了下边界,iv.   新形态要使用的方格中已有其它方块1:表示可行
*/
bool check()
{for (int i = 0; i < 4; i++)if (a[i].x < 0 || a[i].x >= N || a[i].y >= M) return 0;else if (field[a[i].y][a[i].x]) return 0;return 1;
};

3. 进入循环后,首先看事件获取部分的代码

Event e;
/*按照俄罗斯方块游戏的按键习惯一般左键、右键、上键习惯性都是一下一下按所以采用window.pollEvent的方式来获取而下键一般都是按住不动所以可以采用Keyboard::isKeyPressed来获取
*/
while (window.pollEvent(e))
{if (e.type == Event::Closed)window.close();if (e.type == Event::KeyPressed)if (e.key.code == Keyboard::Up) rotate = true;else if (e.key.code == Keyboard::Left) dx = -1;else if (e.key.code == Keyboard::Right) dx = 1;
}
//按住下键不松手
if (Keyboard::isKeyPressed(Keyboard::Down)) delay = 0.05;

4.随后是重头戏,update部分的内容。update主要分为四部分:方块的左右移动,方块的旋转,方块的下落,和整行的检测,先看关于方块左右移动的处理:

/*b[i]是a[i]的备份如果一旦a[i]表示的方块新状态不能通过check的检测那么a[i]通过b[i]恢复到原状
*/
for (int i = 0; i < 4; i++) { b[i] = a[i]; a[i].x += dx; }
//如果check的返回值为0(0是false)
if (!check()) for (int i = 0; i < 4; i++) a[i] = b[i];

然后是方块的旋转:

/*游戏的旋转采用顺时针旋转45度的方式方块的旋转中心点是a[1]无论旋转多少次a[1]的(x,y)始终不会改变而a[0],a[2],a[3]的(x,y)会产生有规律的变化
*/
if (rotate)//顺时针45度 组成方块的4个tile会发生x,y的互换变化
{//方块的a[1]TILE是核心方块,无论怎么rotate,它不会变Vector2i p = a[1]; //center of rotation//组成方块的每个tile(x,y)的变化规律,有点像旋转后,y变x,而x变为yfor (int i = 0; i < 4; i++){int x = a[i].y - p.y;int y = a[i].x - p.x;a[i].x = p.x - x;a[i].y = p.y + y;}//a[i]如果无法通过check,就只能继续保持原状if (!check()) for (int i = 0; i < 4; i++) a[i] = b[i];
}

方块的下落:

if (timer > delay)//下落是程序根据时间控制的(下键会缩短方块下落的delay)
{for (int i = 0; i < 4; i++) { b[i] = a[i]; a[i].y += 1; }if (!check())//下落的时候如果没有通过check,只可能是触底了或者碰到field[][]已有内容了{//那么此时方块的四个tile就是field中的内容了//修改field[][]的数值,记录该方块四个tile的颜色for (int i = 0; i < 4; i++) field[b[i].y][b[i].x] = colorNum;//开始生成下一个新的方块colorNum = 1 + rand() % 7;int n = rand() % 7;for (int i = 0; i < 4; i++){//根据变量figures中记载的数字,生成新方块的4个tile的位置情况a[i].x = figures[n][i] % 2;//1 1 0 1a[i].y = figures[n][i] / 2;//1 2 2 3}}//游戏计时归零,等待下一次累计到delay,新方块下落timer = 0;
}

检测现在在游戏方格中是否有整行的方块出现了,如果出现要想办法消除:

//从下往上查是否有满行的
//M的设定值是20,那么最后一行就是19
int k = M - 1;
for (int i = M - 1; i > 0; i--)
{int count = 0;for (int j = 0; j < N; j++){//统计这一行field[i][j]不为0的个数if (field[i][j]) count++;//最精髓的一行在这里!//我们假设现在游戏中,随着这次方块下落,18行19行满了应该消除//现在是第一次循环,k为19,i为19//将第19行内容复制到了第19行//因为19行满行,所以不会k--,第二次循环//k为19,i为18//这样就将第18行的内容复制到了第19行//因为第18行满行,所以不会k--,开始第三次循环//k为19,i为17//这样就将第17行的内容复制到了第19行//按照我们的假定,第17行不是满行,所以会k--,k变为18后,开始第三次循环//k为18,i为16//这样就将第16行的内容复制到了第18行//依次类推//在后面执行绘制环节的时候,会发现要绘制的最后一行是这次整行检查前第17行field[k][j] = field[i][j];}if (count < N) k--;//不满行的情况下k--
}

5. 最后绘制环节唯一要注意的就是偏移(28,31)

for (int i = 0; i < M; i++)for (int j = 0; j < N; j++){if (field[i][j] == 0) continue;//一个色块18*18s.setTextureRect(IntRect(field[i][j] * 18, 0, 18, 18));s.setPosition(j * 18, i * 18);//offset(有背景,所以方块并不是从(0,0)开始出现的。//以(0,0)计算好位置后,偏移(28,31)真正绘制)s.move(28, 31); window.draw(s);}

通过以上的分析,我们就能发现目前代码中存在的纰漏:

1. 绘制方块时,组成方块的四个tile的位置是记录在a[i]中的。但是a[i]初始化之后,第一次绘制之前,并没有根据figures中的预定内容进行四个tile的位置设置。所以,第一个绘制出来的图形是四个tile重叠在一起的一个“小方块”。

解决方法就是在while总线启动前,为方块做一次正式的初始化。

colorNum = 1 + rand() % 7;
int n = rand() % 7;
for (int i = 0; i < 4; i++)
{a[i].x = figures[n][i] % 2;a[i].y = figures[n][i] / 2;
}while (window.isOpen())....

2.显然这个游戏没有结束机制。根据update中的整行消除机制我们可以推测出游戏的结束机制。按照现在的整行消除机制,其实隐含了一个条件,就是至少要有一个全空的行才可以(也就是至少第0行,即游戏窗格的最上面一行要保持全空才可以),否则是无法实现下移效果的。换句话说,为了避免这个bug的出现,一旦第0行出现了不全空,就意味着游戏应该结束了。

解决方法就是在update时,加入检测对游戏窗格第一行的内容检测机制:

for (int i = 0; i < N; i++)
{if (field[0][i]){//因为没有适当的Texture和字体//所以这里就粗暴的关闭游戏窗,并在控制台输出游戏结束的提示window.close();printf("Game Over");}
}

3. 另外从游戏体验上,每个新方块总是从游戏窗格的最右侧出现。这个感觉不太好,应该从中间或者合理的随机位置出现比较好。

SFML1 俄罗斯方块代码解析相关推荐

  1. matrix_multiply代码解析

    matrix_multiply代码解析 关于matrix_multiply 程序执行代码里两个矩阵的乘法,并将相乘结果打印在屏幕上. 示例的主要目的是展现怎么实现一个自定义CPU计算任务. 参考:ht ...

  2. CornerNet代码解析——损失函数

    CornerNet代码解析--损失函数 文章目录 CornerNet代码解析--损失函数 前言 总体损失 1.Heatmap的损失 2.Embedding的损失 3.Offset的损失 前言 今天要解 ...

  3. 视觉SLAM开源算法ORB-SLAM3 原理与代码解析

    来源:深蓝学院,文稿整理者:何常鑫,审核&修改:刘国庆 本文总结于上交感知与导航研究所科研助理--刘国庆关于[视觉SLAM开源算法ORB-SLAM3 原理与代码解析]的公开课. ORB-SLA ...

  4. 用VB6写的一个简单俄罗斯方块代码

    网络上有很多俄罗斯方块代码.它们大都为了视觉效果,程序比较复杂,不利于学习游戏编程.所以我写了个简单俄罗斯方块代码,尽量用VB本身的功能,没有复杂的DirectX. 下载(注意修改下载后的扩展名) m ...

  5. java获取object属性值_java反射获取一个object属性值代码解析

    有些时候你明明知道这个object里面是什么,但是因为种种原因,你不能将它转化成一个对象,只是想单纯地提取出这个object里的一些东西,这个时候就需要用反射了. 假如你这个类是这样的: privat ...

  6. python中的doc_基于Python获取docx/doc文件内容代码解析

    这篇文章主要介绍了基于Python获取docx/doc文件内容代码解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 整体思路: 下载文件并修改后缀 ...

  7. mongoose框架示例代码解析(一)

    mongoose框架示例代码解析(一) 参考: Mongoose Networking Library Documentation(Server) Mongoose Networking Librar ...

  8. ViBe算法原理和代码解析

    ViBe - a powerful technique for background detection and subtraction in video sequences 算法官网:http:// ...

  9. 【Android 逆向】使用 Python 代码解析 ELF 文件 ( PyCharm 中进行断点调试 | ELFFile 实例对象分析 )

    文章目录 一.PyCharm 中进行断点调试 二.ELFFile 实例对象分析 一.PyCharm 中进行断点调试 在上一篇博客 [Android 逆向]使用 Python 代码解析 ELF 文件 ( ...

  10. 密码算法中iv值是什么_?标检测中的?极?值抑制算法(nms):python代码解析

    ⾮极⼤值抑制(Non-Maximum Suppression)原理 ⾮极⼤值抑制,顾名思义,找出极⼤值,抑制⾮极⼤值.这种思路和算法在各个领域中应⽤⼴泛,⽐如边缘检测算法canny算⼦中就使⽤了该⽅法 ...

最新文章

  1. 七日Python之路--第八天
  2. [BZOJ4033][HAOI2015]树上染色(树形DP)
  3. P4549-[模板]裴蜀定理
  4. jooq 分页排序_将jOOQ与Spring结合使用:排序和分页
  5. cesium 经纬度绘制点_炫酷大屏地图自定义绘制(一)
  6. Python 绘制散点图
  7. 201671030107 胡文艳 实验十四 团队项目评审课程项目总结
  8. MyEclipse10破解详细说明
  9. GitHub图片加载不出来解决方案(超详细图文教程)
  10. 创业者应该问投资人的10个问题
  11. android开发,如何给app授予系统权限
  12. SQL Server 2005“备份集中的数据库备份与现有的数据库不同”解决方法 详细出处参考:http://www.jb51.net/article/19233.htm
  13. 寒门再难出贵子?学会这个方法,跨越阶层不是梦
  14. 连米哈游都成了第二,这个一刀999的页游大王把老外吃透了。
  15. 序列划分c语言,看懂了这些,你对缠论中的线段划分就基本掌握了!
  16. 元宇宙游戏项目:Decentraland(治理通证:MANA)
  17. python 键盘记录_记录键盘敲击次数 python实现
  18. Go 语言入门三部曲(一):能看懂 Go 语言
  19. 【ElementUI】el-table 的表头和内容列不对齐
  20. 【obs-studio开源项目从入门到放弃】预览窗口中source的UI操作绘制处理

热门文章

  1. Linux系统下烧录系统镜像
  2. Xshell和Xftp免费版下载安装
  3. 课程设计之图书管理系统C#实现
  4. PHP直播聊天室源码/财经直播源码/房间多开/游客互动/聊天审核
  5. Java核心技术11 | Java IO
  6. java写入excel乱码_Java导出Excel解决乱码及导出文件打开不可读需修复的问题
  7. html的选择字体样式代码,html 常用字体(示例代码)
  8. Swift - 图片去色 图片灰色显示
  9. 汇编语言王爽实验十三
  10. Java 数据库编程专栏 目录