前言
想看正文的请跳过。热衷小游戏的我又回来了!上次准备给飞机大战小游戏升级,发现写的一坨,难维护拓展,吸取上次教训,这次的清爽多啦!(抽时间把飞机大战重写,再加上联机功能吧),分享给感兴趣的小伙伴,欢迎引用转载。
小程序概述
空间随机撒点,点再空间随机运动,用户鼠标放在画布上,自动连接与用户鼠标较近的点,并连接那些点中距离较近的点,效果示意图如下:
模块简介
类图不完整,仅供参考,详见代码
1.Points模块功能:(线程一)
(1)维护空间所有点的x,y,z随机运动,撞墙反弹
(2)计算所有点的自身属性,比如半径,离目标的距离等
(3)订阅发布模式通知link,那些点是近距离点,这些点会被串在一个单项链表上
2.Link模块功能:(线程二)
(1)维护一个目标点集合,Points模块引起变更
(2)根据目标点集合,计算需要绘图的线条和圆,并写入shareddata中
3.SharedData模块:(单例)
(1)维护一个需要绘图的线条和圆的集合,提供高效的读写锁,供其他模块存取
(2)维护一个用户数据源(当前为鼠标信息),供其他模块存取
4.Draw模块:(线程三)
不断地从共享内存中读取数据,并绘画
代码部分
头文件依赖:#include<easyx.h>
绘图专用#include<thread>
开线程用#include<shared_mutex>
读写锁
小程序的绘图是由easyx.h实现的,可以换其他绘图工具,逻辑不变
common.h
一些通用的宏定义,point点的实现等等
#ifndef __SYAO_COMMON_H_
#define __SYAO_COMMON_H_
#include <cstdlib>
#include <cmath>
/*********************** Micro Defination ***************************/
//绘图相关
#define CRICLE_RADIUS_RATIO 1 //圆半径和z坐标比率
#define CRICLE_RADIUS_MIN 1
#define CRICLE_RADIUS_MAX 5
#define MAX_X 1200 //三坐标限幅,z为正负max
#define MAX_Y 750
#define MAX_Z 50
//计算相关
#define DIS_TO_TARGET_LIMIT 100 //距离目标点100以内为link点
#define DIS_LINK 30 //link于link之间连线的距离
#define POINTS_NUMBER 400 //撒点的数量
#define LINKS_MAX_NUMBER 50 //最大连线点数量
#define CALC_PERIOD 30 //计算周期(ms)
#define DRAW_PERIOD 10 //绘图周期(ms)
#define MOVE_PERIOD 40 //运动周期(ms)
#define INF 2147483647 //正无穷
#define DINF -2147483647
enum
{
CRESH_X = 1,
CRESH_Y ,
CRESH_Z
};
enum color
{
BLK = 0,
RED,
GREEN,
YELLOW,
WRITE,
};
void funAppend(int& x);
/************************ Struct Defination *************************/
typedef struct Point
{
//相关参数
int _ix ;
int _iy ;
int _iz ;
int _iDisToTarget;
int _iCricleRadius; //圆半径
int _vx;
int _vy;
int _vz;
Point* _ptrNext;
bool _bIsLink;
//方法
Point():_iDisToTarget(INF),_iCricleRadius(CRICLE_RADIUS_MIN),_ptrNext(nullptr),_bIsLink(false)
{
//位置随机
_ix = rand()%MAX_X;
_iy = rand()%MAX_Y;
_iz = rand()%MAX_Z;
funAppend(_iz);
//速度随机(理想情况下,点10s跑完整张图,开始反弹)
int tmpTimes = 10 * 1000 / CALC_PERIOD;
int limitVx = MAX_X / tmpTimes;
int limitVy = MAX_Y / tmpTimes;
int limitVz = MAX_Z / tmpTimes;
_vx = static_cast<int>(rand() % limitVx);
_vy = static_cast<int>(rand() % limitVy);
_vz = 3;
if (_vx == 0)
{
_vx = 1;
}
if (_vy == 0)
{
_vy = 1;
}
if (_vz == 0)
{
_vz = 1;
}
funAppend(_vx);
funAppend(_vy);
funAppend(_vz);
}
void reset()
{
_iDisToTarget = INF;
_iCricleRadius = CRICLE_RADIUS_MIN;
_ptrNext = nullptr;
_bIsLink = false;
}
//该次move后和墙壁发生碰撞,则取消move,ret交给上层逻辑做反射处理
int move()
{
int x = _ix + _vx*CALC_PERIOD;
if (x < 0 || x >MAX_X)
{
return CRESH_X;
}
int y = _iy + _vy*CALC_PERIOD;
if (y < 0 || y >MAX_Y)
{
return CRESH_Y;
}
int z = _iz + _vz*CALC_PERIOD;
if (z < -MAX_Z || z > MAX_Z)
{
return CRESH_Z;
}
_ix = x;
_iy = y;
_iz = z;
return 0;
}
void calcCricleRadius()
{
if( _iz < -10 )
{
_iDisToTarget = CRICLE_RADIUS_MIN;
}
else if( _iz > 10 )
{
_iDisToTarget = CRICLE_RADIUS_MAX;
}
else
{
_iDisToTarget = static_cast<int>((100 + _iz)/CRICLE_RADIUS_RATIO) ;
}
}
void calcDistanceToTarget(int in_x,int in_y )
{
int dx = abs(in_x - _ix );
int dy = abs(in_x - _iy );
int dz = abs(_iz); //目标点 z坐标恒为0
_iDisToTarget = static_cast<int>(sqrt( dx*dx + dy*dy + dz*dz ));
if(_iDisToTarget < DIS_TO_TARGET_LIMIT )
{
_bIsLink = true;
}
}
}Point;
#endif
Point.h
点集合的管理和运动模拟
#ifndef _SYAO_POINTS_H_
#define _SYAO_POINTS_H_
#include "common.h"
#include "link.h"
#include <vector>
#include <string>
#include <thread>
class cLink;
class cPoints
{
public:
cPoints(cLink* ptr);
~cPoints();
void initPoints();
void start();
private:
void pointsMove(); //点的运动模拟,撞墙反射
void pointsCalc(); //点的参数计算
void linkUpdate();
void pointsReflect(Point& pointRef ,int param); //反射计算
void run();
private:
std::vector<Point> m_vecPoints; //所有点集合
Point m_StrLinkHead; //待链接节点链表
cLink* m_ptrLink;
std::thread m_thread;
};
#endif
point.cc
#include "points.h"
#include "SharedData.h"
#include <chrono>
#include <thread>
#include <iostream>
using namespace std;
cPoints::cPoints(cLink* ptr):m_ptrLink(ptr)
{
//初始化
}
cPoints::~cPoints()
{
m_thread.join();
}
void cPoints::start()
{
m_thread = std::thread(std::bind(&cPoints::run, this));
}
void cPoints::run()
{
for( ; ;)
{
try
{
pointsMove();
pointsCalc();
linkUpdate();
std::cout << "points--++++++++++++" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(MOVE_PERIOD)); //计算周期
}
catch(std::exception& e)
{
std::cout << "catch exception: " << e.what() << std::endl;
}
catch (...)
{
std::cout << "catch unknown exception" << std::endl;
}
}
}
void cPoints::initPoints()
{
for(int i=0; i<POINTS_NUMBER ;i++)
{
m_vecPoints.push_back(Point());
}
}
/*
*function:
* 模拟所有点的自由运动,包括点撞墙后的反弹
**/
void cPoints::pointsMove()
{
int ret = 0;
for(auto& x: m_vecPoints )
{
ret = x.move();
if (ret != 0)
{
pointsReflect(x, ret);
}
}
}
/*
*function:
* //反射计算
**/
void cPoints::pointsReflect(Point& pointRef, int param)
{
switch (param)
{
case 1:
pointRef._vx = -pointRef._vx;
break;
case 2:
pointRef._vy = -pointRef._vy;
break;
case 3:
pointRef._vz = -pointRef._vz;
break;
default:
break;
}
}
/*
* function:
* 1. 计算每个点到目标点的距离
* 2. 计算该点在此处的绘图尺寸
* 3. 构建连线点链表
*/
void cPoints::pointsCalc()
{
MOUSEMSG tmpMsg;
CSharedData::getInstance()->updateUserPos();
CSharedData::getInstance()->getUserPos(tmpMsg);
m_StrLinkHead._ptrNext = nullptr;
Point* ptrTmp = &m_StrLinkHead;
for (auto &x : m_vecPoints)
{
x.reset();
x.calcCricleRadius();
x.calcDistanceToTarget(tmpMsg.x , tmpMsg.y );
if (x._bIsLink)
{
ptrTmp->_ptrNext = &x;
ptrTmp = ptrTmp->_ptrNext;
}
}
}
/*
* function:
* 更新link的数据
*/
void cPoints::linkUpdate()
{
if (m_ptrLink == nullptr)
{
std::cout << "error m_ptrLink Init error" << std::endl;
return;
}
//构建数据更新link
std::vector<Point> vecLink;
Point* ptrTmp = m_StrLinkHead._ptrNext;
while (ptrTmp != nullptr)
{
vecLink.push_back(*ptrTmp);
ptrTmp = ptrTmp->_ptrNext;
}
m_ptrLink->updateLinkPoint(vecLink);
}
link.h
计算连线的点及圆
#ifndef __SYAO_LINK_H_
#define __SYAO_LINK_H_
#include "common.h"
#include "SharedData.h"
#include <thread>
#include <shared_mutex>
#include <vector>
/***************************
* 功能描述:
* 1.维护一个待连接点集合的m_vecLink
* 2.把要绘图的数据写入shareddata
*/
class cLink
{
public:
cLink();
~cLink();
void updateLinkPoint(std::vector<Point>& vecIn );
void start();
private:
void run(); //更新绘图数据线程
void calcPoint();
void calcCircle();
private:
std::vector<Point> m_vecLink;
std::shared_mutex m_muVecLink;
std::thread m_thread;
};
#endif
link.cc
#include "link.h"
#include <iostream>
using namespace std;
cLink::cLink()
{
//do nothing
}
cLink::~cLink()
{
m_thread.join();
}
void cLink::start() //更新绘图数据线程
{
m_thread = std::thread(std::bind(&cLink::run, this));
}
void cLink::updateLinkPoint(std::vector<Point>& vecIn)
{
std::unique_lock<std::shared_mutex> RLock(m_muVecLink);
m_vecLink.swap(vecIn);
}
void cLink::run()
{
for (; ;)
{
try
{
calcCircle();
calcPoint();
std::cout << "Link--------------" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(CALC_PERIOD)); //计算周期
}
catch (std::exception& e)
{
std::cout << "catch exception: " << e.what() << std::endl;
}
catch (...)
{
std::cout << "catch unknown exception" << std::endl;
}
}
}
void cLink::calcPoint()
{
std::vector<Point> tmpVecLink;
{
std::shared_lock<std::shared_mutex> RLock(m_muVecLink);
tmpVecLink = m_vecLink; //较大量运算,拷贝出来释放锁
}
MOUSEMSG mouseMsg;
CSharedData::getInstance()->getUserPos(mouseMsg);
int iPosX = mouseMsg.x;
int iPosY = mouseMsg.y;
std::vector<PointPint> vecPrintPoint;
PointPint tmp;
//link点到目标点的连线
for (auto& x : tmpVecLink)
{
tmp._iX1 = iPosX;
tmp._iY1 = iPosY;
tmp._iX2 = x._ix;
tmp._iY2 = x._iy;
vecPrintPoint.push_back(tmp);
}
//link点到link点之间连线
int size = tmpVecLink.size();
double dDis = 0;
int dx, dy;
for (int i = 0; i < size; ++i)
{
for (int j = i + 1; j < size; ++j)
{
dx = tmpVecLink[i]._ix - tmpVecLink[j]._ix;
dy = tmpVecLink[i]._iy - tmpVecLink[j]._iy;
dDis = sqrt((dx)*(dx)+(dy)*(dy));
if (dDis > DIS_LINK)
{
tmp._iX1 = tmpVecLink[i]._ix;
tmp._iY1 = tmpVecLink[i]._iy;
tmp._iX2 = tmpVecLink[j]._ix;
tmp._iY2 = tmpVecLink[j]._iy;
vecPrintPoint.push_back(tmp);
}
}
}
CSharedData::getInstance()->refreshLine(vecPrintPoint);
}
void cLink::calcCircle()
{
std::vector<CirclePint> vecPrintCircle;
CirclePint tmp;
{
std::shared_lock<std::shared_mutex> RLock(m_muVecLink);
for (auto& x : m_vecLink)
{
tmp._ix = x._ix;
tmp._iy = x._iy;
tmp._iColor = rand() % 5;
tmp._iRadius = x._iCricleRadius;
vecPrintCircle.push_back(tmp);
}
}
CSharedData::getInstance()->refreshCircle(vecPrintCircle);
}
SharedData共享内存
#ifndef __LINK_SHAREDDATE_H_
#define __LINK_SHAREDDATE_H_
#include <easyx.h>
#include <thread>
#include <mutex>
#include <vector>
#include <shared_mutex>
//线条
struct PointPint
{
int _iX1;
int _iY1;
int _iX2;
int _iY2;
};
//圆
struct CirclePint
{
int _ix;
int _iy;
int _iRadius;
int _iColor;
};
//共享数据读写锁
class CSharedData
{
public:
~CSharedData();
static CSharedData* getInstance();
void refreshLine(std::vector< PointPint>& vecIn);
void refreshCircle(std::vector<CirclePint>& vecIn);
void getCircle(std::vector<CirclePint>& in);
void getLine(std::vector< PointPint>& in);
void updateUserPos();
void getUserPos(MOUSEMSG& mouseMsg);
private:
CSharedData();
private:
static CSharedData* m_ptrOne;
static std::mutex m_mutex;
std::vector< PointPint> m_vecLine;
std::shared_mutex m_SMutexLine;
std::vector<CirclePint> m_vecCircle;
std::shared_mutex m_SMutexCircle;
MOUSEMSG m_mouseInfo;
std::shared_mutex m_SMutexUsrPos;
};
#endif
shareddata.cc
#include "SharedData.h"
using namespace std;
CSharedData* CSharedData::m_ptrOne = nullptr;
std::mutex CSharedData::m_mutex;
CSharedData::CSharedData()
{
}
CSharedData::~CSharedData()
{
}
CSharedData* CSharedData::getInstance()
{
if (m_ptrOne == nullptr)
{
std::unique_lock<std::mutex> lock(m_mutex);
if (m_ptrOne == nullptr)
{
m_ptrOne = new CSharedData();
}
return m_ptrOne;
}
else
{
return m_ptrOne;
}
}
//更新鼠标数据
void CSharedData::updateUserPos()
{
if (MouseHit())
{
std::unique_lock<std::shared_mutex> WLock(m_SMutexUsrPos);
m_mouseInfo = GetMouseMsg();
}
}
//获取鼠标数据
void CSharedData::getUserPos(MOUSEMSG& mouseMsg)
{
std::shared_lock<std::shared_mutex> RLock(m_SMutexUsrPos);
mouseMsg = m_mouseInfo;
}
void CSharedData::refreshLine(std::vector< PointPint>& vecIn)
{
std::unique_lock<std::shared_mutex> WLock(m_SMutexLine);
vecIn.swap(m_vecLine);
}
void CSharedData::refreshCircle(std::vector<CirclePint>& vecIn)
{
std::unique_lock<std::shared_mutex> WLock(m_SMutexCircle);
vecIn.swap(m_vecCircle);
}
void CSharedData::getCircle(std::vector<CirclePint>& in)
{
std::shared_lock<std::shared_mutex> RLock(m_SMutexCircle);
in = m_vecCircle;
}
void CSharedData::getLine(std::vector< PointPint>& in)
{
std::shared_lock<std::shared_mutex> RLock(m_SMutexLine);
in = m_vecLine;
}
Draw.h 绘图专用
#ifndef __LINK_DRAW_H__
#define __LINK_DRAW_H__
#include "common.h"
#include "SharedData.h"
#include <easyx.h>
#include <thread>
#include <vector>
class CDraw
{
public:
CDraw();
~CDraw();
void start();
private:
void initMap(); //初始化画布
void getData();
void draw();
void cleanMap();
void run();
private:
std::thread m_thread;
std::vector<PointPint> m_vecPoint;
std::vector< CirclePint> m_vecCircle;
};
#endif
draw.cc
#include "draw.h"
#include <iostream>
#include <chrono>
using namespace std;
void funAppend(int& x)
{
if (rand() % 2)
{
x = -x;
}
}
CDraw::CDraw()
{
initMap();
}
CDraw::~CDraw()
{
m_thread.join();
closegraph();
}
void CDraw::start()
{
m_thread = std::thread(std::bind(&CDraw::run, this));
}
void CDraw::run()
{
for (; ;)
{
try
{
cleanMap();
getData();
draw();
std::cout << "draw" << std::endl;
this_thread::sleep_for(chrono::milliseconds(DRAW_PERIOD));
}
catch (exception& e)
{
cout << "exception:" << e.what() << endl;
}
catch (...)
{
cout << "unknown_exception" << endl;
}
}
}
void CDraw::initMap()
{
initgraph(MAX_X, MAX_Y); //初始化画布
}
void CDraw::getData()
{
CSharedData* pTmp = CSharedData::getInstance();
pTmp->getLine(m_vecPoint);
pTmp->getCircle(m_vecCircle);
}
void CDraw::draw()
{
for (auto& x : m_vecCircle)
{
setfillcolor(GREEN);
solidcircle(x._ix, x._iy, x._iRadius);
}
for (auto& ref : m_vecPoint)
{
setlinecolor(565); // 设置当前线条颜色
line(ref._iX1, ref._iY1, ref._iX2, ref._iY2);
}
FlushBatchDraw();
}
void CDraw::cleanMap()
{
cleardevice();
m_vecCircle.clear();
m_vecPoint.clear();
}
main.cc
#include "link.h"
#include "draw.h"
#include "common.h"
#include "points.h"
#include "SharedData.h"
#include <chrono>
#include <iostream>
using namespace std;
int main()
{
cLink myLink;
cPoints myPoints(&myLink);
CDraw myDraw;
myPoints.initPoints();
myPoints.start();
myLink.start();
myDraw.start();
return 0;
}
结语
网瘾少年的游戏梦!