前言

想看正文的请跳过。热衷小游戏的我又回来了!上次准备给飞机大战小游戏升级,发现写的一坨,难维护拓展,吸取上次教训,这次的清爽多啦!(抽时间把飞机大战重写,再加上联机功能吧),分享给感兴趣的小伙伴,欢迎引用转载。

小程序概述

空间随机撒点,点再空间随机运动,用户鼠标放在画布上,自动连接与用户鼠标较近的点,并连接那些点中距离较近的点,效果示意图如下:

链接javascript 链接小游戏_#define

模块简介

类图不完整,仅供参考,详见代码

链接javascript 链接小游戏_#include_02


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;
}

结语

网瘾少年的游戏梦!