实现思路

这篇笔记给出一个仿 Cocos2d-x 中 DelayTime 的简易定时器实现。思路是用一个 DelayTime 类封装“延迟时间 + 回调函数”,再用一个 FrameManger 管理所有待触发的 DelayTime,在主循环里按照固定频率推进时间,到点就触发回调。

主循环依赖 QueryPerformanceCounter / QueryPerformanceFrequency 做高精度计时,把一次完整的 Loop 看作一帧(目标 60 帧),每帧让队列里所有定时器的 nowTime 自增一次,达到目标帧数就执行回调并重置计数。

完整代码

#include"iostream"
#include"windows.h"
#include "functional"
#include "time.h"
#include "math.h"
using namespace std;

class Ref
{

};



class DelayTime: public Ref
{
public:
	static DelayTime*create(float time, const function<void(void)> &f)
	{
		auto ss = new DelayTime;
		ss->func = f;
		ss->time = time*60;
		return ss;
	}

	function<void()> func;

	void run( )
	{
		func();
	}


	float time;
	float nowTime = 0;

};
int a = 0, b = 0;

class FrameManger :public Ref
{
public:
	FrameManger()
	{
		for (int i = 0; i < 100; i++)
		{
			queue[i] = nullptr;
		}
	}
	void addChild(DelayTime *pChild)
	{
		queue[count++] = pChild;

	}
	void Loop()
	{
		Sleep(rand()%5);
		for (int i = 0;i<count;i++)
		{

			if (queue[i] == nullptr)return;
			queue[i]->nowTime++;
			if (queue[i]->nowTime >= queue[i]->time)
			{
				 queue[i]->run();
				 cout << "    "<<clock()-a<<endl;
				 queue[i]->nowTime = 0;
				 a = clock();

			}
		}


	}
	 DelayTime* queue[100];
	 int count = 0;
};



int main()
{

	FrameManger*fr = new FrameManger;

	fr->addChild(DelayTime::create(1.0, [=]{cout << "callback_1"; }));
	fr->addChild(DelayTime::create(2.0, [=]{cout << "callback_2"; }));

	a = clock();


	LARGE_INTEGER nFreq;
	LARGE_INTEGER nLast;
	LARGE_INTEGER nNow;
	long long Interval;

	QueryPerformanceFrequency(&nFreq);
	Interval = (long long)(1/60.0 * nFreq.QuadPart);
	cout << "x=" << nFreq.QuadPart << endl;
	QueryPerformanceCounter(&nLast);
	while (1)
	{
		QueryPerformanceCounter(&nNow);
		if (nNow.QuadPart - nLast.QuadPart >Interval)//计算cpu频率计数差值
		{
			nLast.QuadPart = nNow.QuadPart;
			fr->Loop();
		}
		else
		{
			Sleep(0);
		}
	}







	system("pause");
	return 0;
}

关键点说明

  • 帧化时间DelayTime::create 里把用户传入的秒数乘以 60(ss->time = time*60),这样内部统一用“帧数”作为时间单位,Loop 每被调用一次就让 nowTime 自增 1。
  • 回调容器function<void()> func 用来存放任意可调用对象,create 接收 const function<void(void)> &f,这样 lambda、函数指针、仿函数都能塞进来。
  • 固定帧率驱动:主循环里用 QueryPerformanceFrequency 拿到计数器频率,Interval = (long long)(1/60.0 * nFreq.QuadPart) 算出“一帧对应多少个高精度计数”,两次 QueryPerformanceCounter 的差值超过 Interval 就推进一帧,从而逼近 60 帧。
  • 调度队列FrameManger 用一个长度 100 的裸指针数组 queue[100] 存放 DelayTime*addChild 按顺序追加,Loop 里遇到空指针就返回,相当于一个最朴素的定长环形任务池。
  • 触发逻辑:当某个 DelayTimenowTime 累积到 time(即达到目标帧数)时,调用 run() 触发回调,然后把 nowTime 归零,使这个定时器变成“周期触发”。
  • 时间误差观察:每次回调都会打印 clock()-a,并把 a 更新为当前 clock(),可以用来观察两次回调之间的真实耗时,验证 60 帧驱动下的调度误差。

使用示例

main 中先创建 FrameManger,再往里添加两个 DelayTime:一个 1 秒后触发 callback_1,一个 2 秒后触发 callback_2。随后进入高精度定时驱动的 while (1) 主循环,由 fr->Loop() 负责按帧推进所有定时器。

这是一个面向学习的最小可运行版本,没有处理定时器的删除、容量扩容、异常回调等问题,但足够说明 DelayTime 类的核心调度思想。