当前位置: 首页 > news >正文

小游戏和GUI编程(4) | 基于 SFML 的黑客帝国字符雨

小游戏和GUI编程(4) | 基于 SFML 的黑客帝国字符雨

文章目录

  • 小游戏和GUI编程(4) | 基于 SFML 的黑客帝国字符雨
    • 1. 简介
    • 2. 规划
    • 3. 一个字符的下落
    • 3. 一个雨滴的下落
    • 4. 每个雨滴都是独一无二的
    • 5. 重构后, 降落多个雨滴
    • 6. 总结
    • 7. 参考

1. 简介

使用 SFML 实现黑客帝国字符雨的动态下落效果。

2. 规划

  • 一个字符下落
  • 一个雨滴的下落
  • 每个雨滴都是独一无二的
  • 重构后, 降落多个雨滴

3. 一个字符的下落

请添加图片描述

显示 SFML 窗口, 在窗口的中轴线上绘制一个降落的 H 字母, 字母是绿色的, 背景是黑色的。 每更新一帧, H 的位置就下降 16 个像素。 H 字母的大小也是 16 像素。

由于降落的比较快, 手动控制帧率为 10 FPS.

关键代码:

window.setFramerateLimit(10);...window.clear();sf::Text text;
text.setFont(font);
text.setFillColor(sf::Color::Green);
text.setCharacterSize(16);
text.setString(std::string(1, 'H'));
text.setPosition(x, y);
y = (y + 16) % win_height;
window.draw(text);window.display();

完整代码:

#include <SFML/Graphics.hpp>int main()
{constexpr int win_width = 640;constexpr int win_height = 480;sf::VideoMode videomode(win_width, win_height);const std::string title = "Matrix Rain SFML";sf::RenderWindow window(videomode, title);window.setFramerateLimit(10);sf::Font font;const std::string asset_dir = "../Resources";if (!font.loadFromFile(asset_dir + "/Arial.ttf")){printf("Error: font not found\n");return 1;}int x = win_width / 2;int y = 0;while (window.isOpen()){sf::Event event;while (window.pollEvent(event)){if (event.type == sf::Event::Closed) { window.close(); }}window.clear();sf::Text text;text.setFont(font);text.setFillColor(sf::Color::Green);text.setCharacterSize(16);text.setString(std::string(1, 'H'));text.setPosition(x, y);y = (y + 16) % win_height;window.draw(text);window.display();}return 0;
}

3. 一个雨滴的下落

一个雨滴的下落, 说的是一个动态的过程, 由于它下落的很快, 可以同时显示它的多个位置, 因此纵向方向绘制多个字符, 就表示出了一个时间间隔内的雨滴降落轨迹。

    std::string seq = "abcdefghijklmnopqrstuvwxyz";for (int i = 0; i < seq.size(); i++){sf::Text text;text.setFont(font);text.setFillColor(sf::Color::Green);text.setCharacterSize(16);text.setString(std::string(1, seq[i]));y = i * 16;text.setPosition(x, y);window.draw(text);}

雨滴轨迹中最下方的是现在的位置, 颜色最深, 最上面的颜色最浅。 因此可以根据雨滴的的长度和位置, 使用渐变的颜色, 来表达雨滴的轨迹:

    std::string seq = "abcdefghijklmnopqrstuvwxyz";for (int i = 0; i < seq.size(); i++){sf::Text text;text.setFont(font);text.setFillColor(sf::Color(0, ((i+1) * 1.0/seq.size())*255, 0));text.setCharacterSize(16);text.setString(std::string(1, seq[i]));y = i * 16;text.setPosition(x, y);window.draw(text);}

雨滴的轨迹, 是从上往下降落的, 因此当我们只关注单个雨滴的颜色最深的绿色字符时, 它的位置应该是从上往下的:

int base_y = 0;while(window.isOpen())
{std::string seq = "hello world";for (int i = 0; i < seq.size(); i++){sf::Text text;text.setFont(font);text.setFillColor(sf::Color(0, ((i+1) * 1.0/seq.size())*255, 0));text.setCharacterSize(16);text.setString(std::string(1, seq[i]));y = i * 16 + base_y;text.setPosition(x, y);window.draw(text);}base_y = (base_y + 16) % win_height;
}

效果:
请添加图片描述

4. 每个雨滴都是独一无二的

每个雨滴对应的字符串是不同的(字符数量, 字符内容), 出现位置也不相同。

对于单个雨滴, 首先随机化它的出现位置, 也就是 x 坐标; 而在这个雨滴降落到窗口最下方之前, x 坐标需要保持不变; 当降落到窗口最下方时, 重新随机化 x 坐标:

x = rand() % (win_width / 16) * 16;while (window.isOpen())
{std::string seq = "hello world";for (int i = 0; i < seq.size(); i++){sf::Text text;text.setFont(font);text.setFillColor(sf::Color(0, ((i+1) * 1.0/seq.size())*255, 0));text.setCharacterSize(16);text.setString(std::string(1, seq[i]));y = i * 16 + base_y;text.setPosition(x, y);window.draw(text);}base_y = base_y + 16;if (base_y >= win_height){base_y = 0;x = rand() % (win_width / 16) * 16;}
}

请添加图片描述

对于字符内容、 字符长度, 也需要在每次雨滴到达窗口最下方的时候更新:

static std::string get_random_seq(int& seqlen)
{seqlen = rand() % 10 + 5;std::string seq(seqlen, ' ');for (int i = 0; i < seq.size(); i++){seq[i] = (rand() % 26) + 'a';}return seq;
}std::string seq = get_random_seq(seqlen);
int base_y = 0;
while (window.isOpen())
{window.clear();for (int i = 0; i < seq.size(); i++){sf::Text text;text.setFont(font);text.setFillColor(sf::Color(0, ((i+1) * 1.0/seq.size())*255, 0));text.setCharacterSize(16);text.setString(std::string(1, seq[i]));y = i * 16 + base_y;text.setPosition(x, y);window.draw(text);}base_y = base_y + 16;if (base_y >= win_height){base_y = 0;x = rand() % (win_width / 16) * 16;seq = get_random_seq(seqlen);}window.display();
}

5. 重构后, 降落多个雨滴

首先, 针对单个雨滴的降落, 把之前的面向过程的写法重构为 OOP 写法. 通过封装为 class Rain, 提供的 drop() 方法完成了渲染, 使得 main loop 代码变得清爽:

#include <SFML/Graphics.hpp>static std::string get_random_seq(int& seqlen)
{seqlen = rand() % 10 + 5;std::string seq(seqlen, ' ');for (int i = 0; i < seq.size(); i++){seq[i] = (rand() % 26) + 'a';}return seq;
}class Rain
{
public:Rain(int a_win_width, int a_win_height, sf::Font& a_font){seqlen = rand() % 10 + 5;seq = get_random_seq(seqlen);x = rand() % (win_width / 16) * 16;font = a_font;base_y = 0;win_width = a_win_width;win_height = a_win_height;}std::string seq;int seqlen;int x;sf::Font font;int base_y;int y = 0;int win_width;int win_height;void drop(sf::RenderWindow& window){for (int i = 0; i < seq.size(); i++){sf::Text text;text.setFont(font);text.setFillColor(sf::Color(0, ((i+1) * 1.0/seq.size())*255, 0));text.setCharacterSize(16);text.setString(std::string(1, seq[i]));y = i * 16 + base_y;text.setPosition(x, y);window.draw(text);}base_y = base_y + 16;if (base_y >= win_height){base_y = 0;x = rand() % (win_width / 16) * 16;seq = get_random_seq(seqlen);}}
};int main()
{srand((unsigned)time(NULL));constexpr int win_width = 640;constexpr int win_height = 480;sf::VideoMode videomode(win_width, win_height);const std::string title = "Matrix Rain SFML";sf::RenderWindow window(videomode, title);window.setFramerateLimit(10);sf::Font font;const std::string asset_dir = "../Resources";if (!font.loadFromFile(asset_dir + "/Arial.ttf")){printf("Error: font not found\n");return 1;}Rain rain(win_width, win_height, font);while (window.isOpen()){sf::Event event;while (window.pollEvent(event)){if (event.type == sf::Event::Closed) { window.close(); }}window.clear(sf::Color::Black);rain.drop(window);window.display();}return 0;
}

其次, 为了渲染多个雨滴, 每个雨滴的都需要执行 drop() 函数。 由于 drop() 函数里的最后判断重置的代码, 和构造函数里重复了, 因此进一步提取出 reset() 成员函数。

最终代码

#include <SFML/Graphics.hpp>static std::string get_random_seq(int& seqlen)
{seqlen = rand() % 20 + 8;std::string seq(seqlen, ' ');for (int i = 0; i < seq.size(); i++){seq[i] = (rand() % 26) + 'a';}return seq;
}class Rain
{
public:Rain(int a_win_width, int a_win_height, sf::Font& a_font){srand((unsigned)time(NULL));font = a_font;win_width = a_win_width;win_height = a_win_height;reset();}void reset(){seq = get_random_seq(seqlen);x = rand() % (win_width / 16) * 16;base_y = 0;}std::string seq;int seqlen;int x;sf::Font font;int base_y;int y = 0;int win_width;int win_height;void drop(sf::RenderWindow& window){for (int i = 0; i < seq.size(); i++){sf::Text text;text.setFont(font);text.setFillColor(sf::Color(0, ((i+1) * 1.0/seq.size())*255, 0));text.setCharacterSize(16);text.setString(std::string(1, seq[i]));y = i * 16 + base_y;text.setPosition(x, y);window.draw(text);if (y >= win_height){reset();break;}}base_y += 16;}
};int main()
{constexpr int win_width = 800;constexpr int win_height = 600;sf::VideoMode videomode(win_width, win_height);const std::string title = "Matrix Rain SFML";sf::RenderWindow window(videomode, title);window.setFramerateLimit(10);sf::Font font;const std::string asset_dir = "../Resources";if (!font.loadFromFile(asset_dir + "/Arial.ttf")){printf("Error: font not found\n");return 1;}std::vector<Rain> rains(40, Rain(win_width, win_height, font));for (int i = 0; i < rains.size(); i++){rains[i].reset();}while (window.isOpen()){sf::Event event;while (window.pollEvent(event)){if (event.type == sf::Event::Closed) { window.close(); }}window.clear(sf::Color::Black);for (auto& rain: rains){rain.drop(window);}window.display();}return 0;
}

最终效果:

请添加图片描述

6. 总结

本文提供了基于 SFML 的仿黑客帝国的雨滴下落效果的的动态窗口显示, 从最简单的单个字符下落显示, 到最终的多雨滴、随机出现位置、序列长度随机化, 每一步都给出了代码和基本的解释。 在简介阶段只做了初步的任务划分, 在每一小节则进一步细化需求, 通过一步或多步实现了效果, 并在必要的时候(单个雨滴到多个雨滴)时, 通过重构代码, 降低了代码调用的复杂度, 从而为多个雨滴的降落效果提供了便利。

7. 参考

  • https://www.cnblogs.com/zjutzz/p/17067234.html

相关文章:

  • 机器学习3----决策树
  • Android java基础_多态性
  • [ubuntu]split命令分割文件
  • Swift 初见
  • MQTT的学习与应用
  • rtt设备io框架面向对象学习-dac设备
  • Unity下使用Sqlite
  • 开发自定义标记应用程序
  • 2024年远控软件年度盘点:安全、稳定、功能之选
  • 柚见(伙伴匹配系统)第三期
  • 动态水印怎么加 怎么去除动态水印 视频剪辑软件 会声会影安激活序列号 会声会影怎么剪辑视频
  • 2024beginCTF-Crypto 复现
  • linuxqq关闭主面板后无法再次打开的问题
  • 五.实战软件部署 4-5MYSQL 5.7版本在ubuntu(WSL环境)安装MYSQL 8.0版本在ubuntu(WSL环境)安装
  • LeetCode、901. 股票价格跨度【中等,单调栈】
  • @jsonView过滤属性
  • 【翻译】babel对TC39装饰器草案的实现
  • 【跃迁之路】【477天】刻意练习系列236(2018.05.28)
  • CSS 三角实现
  • egg(89)--egg之redis的发布和订阅
  • Java精华积累:初学者都应该搞懂的问题
  • Java深入 - 深入理解Java集合
  • jQuery(一)
  • mac修复ab及siege安装
  • Odoo domain写法及运用
  • Vue ES6 Jade Scss Webpack Gulp
  • 从tcpdump抓包看TCP/IP协议
  • 分享一份非常强势的Android面试题
  • 面试总结JavaScript篇
  • 扫描识别控件Dynamic Web TWAIN v12.2发布,改进SSL证书
  • 深度学习在携程攻略社区的应用
  • 深入体验bash on windows,在windows上搭建原生的linux开发环境,酷!
  • 为视图添加丝滑的水波纹
  • 阿里云ACE认证之理解CDN技术
  • ​2021半年盘点,不想你错过的重磅新书
  • ​比特币大跌的 2 个原因
  • # MySQL server 层和存储引擎层是怎么交互数据的?
  • #Ubuntu(修改root信息)
  • #我与Java虚拟机的故事#连载10: 如何在阿里、腾讯、百度、及字节跳动等公司面试中脱颖而出...
  • (003)SlickEdit Unity的补全
  • (27)4.8 习题课
  • (二)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (附源码)springboot宠物管理系统 毕业设计 121654
  • (力扣)1314.矩阵区域和
  • (全部习题答案)研究生英语读写教程基础级教师用书PDF|| 研究生英语读写教程提高级教师用书PDF
  • (四)鸿鹄云架构一服务注册中心
  • (一) storm的集群安装与配置
  • (转) SpringBoot:使用spring-boot-devtools进行热部署以及不生效的问题解决
  • (转)一些感悟
  • .bat批处理(三):变量声明、设置、拼接、截取
  • .NET Core跨平台微服务学习资源
  • .NET开源项目介绍及资源推荐:数据持久层 (微软MVP写作)
  • /etc/sudoers (root权限管理)
  • /ThinkPHP/Library/Think/Storage/Driver/File.class.php  LINE: 48
  • /var/spool/postfix/maildrop 下有大量文件