/*
* 程序名称:字符雨
* 环 境:VS2019, EasyX 2021 版, C++11
* 作 者:孙小鱼(QQ:1226598193)
* 版 权:作者原创,无抄袭,不涉及版权问题
* 完成时间:2021-12-07
*/
#include <iostream>
#include <graphics.h>
#include <Windows.h>
#include <vector>
#include <string>
#include <map>
#include <cmath>
#include <conio.h>
#include <ctime>
using namespace std;
#pragma region program config
bool useNoBorderWindow = true;// 是否使用无边框窗口(true 为去除边框)
int xScreen = GetSystemMetrics(SM_CXSCREEN);// 屏幕宽度(像素)
int yScreen = GetSystemMetrics(SM_CYSCREEN);// 屏幕高度(像素)
const int rainMaxNum = 100;// 屏幕上随机生成的雨滴的最大数量(太大会影响性能)
const int rainMinNum = 50;// 屏幕上随机生成的雨滴的最小数量
const int characterMaxNum = 25;// 每个雨滴中的字符最大数量(太大会影响性能)
const int characterMinNum = 5;// 每个雨滴中的字符最小数量
const float randomUp = -20.0f;// 随机生成节点的上边界
const float randomLeft = 0;// 随机生成节点的左边界
const float randomBottom = 0;// 随机生成节点的下边界
const float randomRight= (float)xScreen;// 随机生成节点的右边界
const float rainMaxSize= 55;// 字体最大大小
const float rainMinSize= 25;// 字体最小大小
const float rainMaxSpeed = yScreen / 1.2f;// 像素 /(秒)
const float rainMinSpeed = yScreen / 8.0f;
const float ratioWH = 0.5f;// 字符得宽高比
#pragma endregion
#pragma region Graph Function
HWND hWnd;
inline void syOuttextxy(float x, float y, TCHAR ch);
void CreateCanvas();
void DestoryCanvas();
#pragma endregion
struct Rect
{
union
{
struct { float x1, y1, x2, y2; };
struct { float left, up, right, bottom; };
};
};
struct Point
{
float x, y;
Point(float _x = 0.0f, float _y = 0.0f) : x(_x), y(_y) {}
Point(const Point& pos) : x(pos.x), y(pos.y) {}
};
// 每一个可绘制对象都要继承自该类
class DrawAble
{
protected:
Point m_position;
public:
voidSetPosition(const Point& pos) { m_position = pos; }
PointGetPosition() { return m_position; }
public:
DrawAble() : m_position({ 0.0f, 0.0f }) {}
virtual void Draw()= 0;
virtual void Update()= 0;
virtual ~DrawAble() {};
};
struct CompareRainNode;// 超前声明
/* 字符结构 */
class RainNode : public DrawAble
{
friend struct CompareRainNode;
private:
vector<TCHAR>m_characters;// 所有的字符
COLORREFm_color;// 字符颜色
floatm_size;// 字符大小
floatm_speed;// 字符下落速度
boolm_isActive;// 当前字符是否还在屏幕显示范围内
public:
void Draw() override;
void Update() override;
public:
RainNode();
void SetAttributes(const vector<TCHAR>& characters, COLORREF color, float size, float speed, Point pos = { 0.0f, 0.0f });
bool IsActive() { return m_isActive; }
void SetActive(bool active) { m_isActive = active; }
private:
void _UpdateActive();// 更新是否在屏幕内的标志
float Fx(float x) { return x * x; }// 字符串变色的插值函数
};
/* 使用 Map 之前,我们需要提供比较运算符*/
struct CompareRainNode
{
bool operator()(const RainNode* a,const RainNode* b) const
{
return a < b;
}
};
/* 字符管理 */
class RainManager
{
private:
static RainManager* m_instance;
vector<RainNode*> m_rainPool;
map<RainNode*, DrawAble*, CompareRainNode> m_rains;
public:
static RainManager& GetInstance();
~RainManager();
void Frame();
private:
RainManager();
RainManager(const RainManager& rainManager) = delete;
RainManager& operator=(const RainManager& rainManager) = delete;
void Update();// 每一帧对所有的字符雨进行更新、删除不在屏幕上的雨滴
void Draw();// 每一帧进行绘制
private:
void* _GetRainFromPool();// 从雨滴池子中获取一个新雨滴
void _GenRain(float p);// 以概率函数 Fx(x) <= p 产生一个新雨滴, p = [0, 1]
float _Fx(float x);// 定义域为 x = [0, 1], 值域为 Fx = [0, 1] 的函数
void _RandomInitRainNode(const Rect& rec, RainNode & rainNode);// 在 rec 区域内随机产生一个字符雨节点
};
/* 帧率计算 */
class Time
{
private:
static Time* m_instance;
public:
static Time& GetInstance();
private:
double m_deltaTime;
double m_time;
LARGE_INTEGER m_tickPerSecond;
LARGE_INTEGER m_curTick;
LARGE_INTEGER m_lastTick;
LARGE_INTEGER m_startTick;
public:
void Update();// 每帧更新时间
double deltaTime();
double time();
private:
Time();
Time(const Time& t) = delete;
Time& operator=(const Time& t) = delete;
};
int main()
{
float nextTime = 0;
double frame;
TCHAR tcFrame[16];
CreateCanvas();
while (!_kbhit())
{
cleardevice();// 清屏
Time::GetInstance().Update();// 更新时间
RainManager::GetInstance().Frame();// 字符每帧的更新、绘制
if (Time::GetInstance().time() > nextTime)// 绘制 FPS
{
nextTime += 1.0f;
frame = 1.0 / Time::GetInstance().deltaTime();
wsprintf(tcFrame, L"FPS:%d\n", (int)(frame + 0.5));
}
settextcolor(GREEN);
settextstyle(15, 0, L"宋体");
outtextxy(1, 1, tcFrame);
FlushBatchDraw();
}
DestoryCanvas();
return 0;
}
#pragma region Graph Function
inline void syOuttextxy(float x, float y, TCHAR ch)
{
outtextxy((int)(x + 0.5f), (int)(y + 0.5f), ch);
}
void CreateCanvas()
{
hWnd = initgraph(xScreen, yScreen);
if (useNoBorderWindow)
{
// 设置窗口没有边界、最大化窗口
int screenWidth = GetSystemMetrics(SM_CXSCREEN);// 屏幕宽度(像素)
int screenHeight = GetSystemMetrics(SM_CYSCREEN);// 屏幕高度(像素)
SetWindowLong(hWnd, GWL_STYLE, GetWindowLong(hWnd, GWL_STYLE) & (~WS_CAPTION));
SetWindowPos(hWnd, HWND_TOP, (screenWidth - xScreen) >> 1, (screenHeight - yScreen) >> 1, xScreen, yScreen, SWP_SHOWWINDOW);
}
BeginBatchDraw();
setbkmode(TRANSPARENT);
srand((unsigned int)time(nullptr));
Time::GetInstance();
}
void DestoryCanvas()
{
EndBatchDraw();
closegraph();
}
#pragma endregion
2022年01月10日 11点01分
3