源码:CCDirector.cpp
void
DisplayLinkDirector
::mainLoop()
{
if
(_purgeDirectorInNextLoop)
{
_purgeDirectorInNextLoop =
false
;
purgeDirector();
}
else
if
(! _invalid)
{
drawScene();
// release the objects
PoolManager::getInstance()->getCurrentPool()->clear();
}
}
上述代码中,drawScene函数主要实现游戏中的Scheduler调度和展示对象渲染;PoolManager::getInstance()->getCurrentPool()->clear()一句实现的便是Cocos2dx自动释放池对象的释放。
mainLoop的调用,在不同系统平台下实现的方式有所不同。
Windows版本
源码:CCApplication-win32.cpp
int
Application
::run()
{
... ...
LARGE_INTEGER
nLast;
LARGE_INTEGER
nNow;
QueryPerformanceFrequency(&nFreq);
QueryPerformanceCounter(&nLast);
... ...
auto
glview = director->getOpenGLView();
... ...
while
(!glview->windowShouldClose())
{
QueryPerformanceCounter(&nNow);
if
(nNow.QuadPart - nLast.QuadPart > _animationInterval.QuadPart)
{
nLast.QuadPart = nNow.QuadPart - (nNow.QuadPart % _animationInterval.QuadPart);
director->mainLoop();
glview->pollEvents();
}
else
{
Sleep(1);
}
}
.... ....
}
void
Application
::setAnimationInterval(
double
interval
)
{
LARGE_INTEGER
nFreq;
QueryPerformanceFrequency(&nFreq);
_animationInterval.QuadPart = (
LONGLONG
)(
interval
* nFreq.QuadPart);
}
上述代码中,Application::run函数由main.cpp入口函数触发;Application::setAnimationInterval函数则由Director::setAnimationInterval函数触发,所以当我们更改游戏帧频时,Application中
_animationInterval成员的值也会被改变。
在windows系统平台下,
QueryPerformanceFrequency作用是获取系统高精度计数器的频率,即系统每秒钟触发的高精度计数器计数的次数;而
QueryPerformanceCounter这两个方法分别用来获取系统高精度计时器的当前计数。关于这两个接口的更多介绍可百度查询。
由此可知,
_animationInterval.QuadPart中存储的值为游戏单帧时间对应的高精度计时器计数。
因此做语句nNow.QuadPart - nLast.QuadPart > _animationInterval.QuadPart的判断,目的在于如果前后两次调用的间隔时间超过了游戏设置的帧频时间,则进入执行游戏流程,否则,进入Sleep(1)操作,将控制权交给其他程序。
Android版本
源代码:Cocos2dxRenderer.java (位于PROJECT_DIR\cocos2d\cocos\platform\android\java\src\org\cocos2dx\lib目录下,其中PROJECT_DIR为项目路径)
@Override
public void onDrawFrame(final GL10 gl) {
/*
* No need to use algorithm in default(60 FPS) situation,
* since onDrawFrame() was called by system 60 times per second by default.
*/
if (sAnimationInterval <= 1.0 / 60 * Cocos2dxRenderer.NANOSECONDSPERSECOND) {
Cocos2dxRenderer.nativeRender();
} else {
final long now = System.nanoTime();
final long interval = now - this.mLastTickInNanoSeconds;
if (interval < Cocos2dxRenderer.sAnimationInterval) {
try {
Thread.sleep((Cocos2dxRenderer.sAnimationInterval - interval) / Cocos2dxRenderer.NANOSECONDSPERMICROSECOND);
} catch (final Exception e) {
}
}
/*
* Render time MUST be counted in,or the FPS will slower than appointed.
*/
this.mLastTickInNanoSeconds = System.nanoTime();
Cocos2dxRenderer.nativeRender();
}
}
public void onDrawFrame(final GL10 gl) {
/*
* No need to use algorithm in default(60 FPS) situation,
* since onDrawFrame() was called by system 60 times per second by default.
*/
if (sAnimationInterval <= 1.0 / 60 * Cocos2dxRenderer.NANOSECONDSPERSECOND) {
Cocos2dxRenderer.nativeRender();
} else {
final long now = System.nanoTime();
final long interval = now - this.mLastTickInNanoSeconds;
if (interval < Cocos2dxRenderer.sAnimationInterval) {
try {
Thread.sleep((Cocos2dxRenderer.sAnimationInterval - interval) / Cocos2dxRenderer.NANOSECONDSPERMICROSECOND);
} catch (final Exception e) {
}
}
/*
* Render time MUST be counted in,or the FPS will slower than appointed.
*/
this.mLastTickInNanoSeconds = System.nanoTime();
Cocos2dxRenderer.nativeRender();
}
}
... ...
private static native void nativeRender();
... ...
public static void setAnimationInterval(final double animationInterval) {
Cocos2dxRenderer.sAnimationInterval = (long) (animationInterval * Cocos2dxRenderer.NANOSECONDSPERSECOND);
}
源代码:Java_org_cocos2dx_lib_Cocos2dxRenderer.cpp(位于PROJECT_DIR\cocos2d\cocos\platform\android\jni目录下)
JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeRender(JNIEnv* env) {
cocos2d::Director::getInstance()->mainLoop();
}
cocos2d::Director::getInstance()->mainLoop();
}
源代码:CCApplication-android.cpp(位于PROJECT_DIR\cocos2d\cocos\platform\android目录下
)
void Application::setAnimationInterval(double interval)
{
JniMethodInfo methodInfo;
if (! JniHelper::getStaticMethodInfo(methodInfo,"org/cocos2dx/lib/Cocos2dxRenderer","setAnimationInterval","(D)V"))
{
CCLOG("%s %d: error to get methodInfo",__FILE__,__LINE__);
}
else
{
methodInfo.env->CallStaticVoidMethod(methodInfo.classID,methodInfo.methodID,interval);
}
}
{
JniMethodInfo methodInfo;
if (! JniHelper::getStaticMethodInfo(methodInfo,"org/cocos2dx/lib/Cocos2dxRenderer","setAnimationInterval","(D)V"))
{
CCLOG("%s %d: error to get methodInfo",__FILE__,__LINE__);
}
else
{
methodInfo.env->CallStaticVoidMethod(methodInfo.classID,methodInfo.methodID,interval);
}
}
显然,mainLoop函数是由Cocos2dxRenderer类中的JNI接口Cocos2dxRender.nativeRender方法触发(若不清楚JNI接口为何物,请移步
http://www.jb51.cc/article/p-mjmbpfkl-vu.html),而进一步Cocos2dxRender.nativeRender在Cocos2dxRenderer类中的onDrawFrame中被调用。
因此mainLoop函数的触发就受Cocos2dxRenderer.onDrawFrame方法所控制。GLSurfaceView.Renderer接口中的onDrawFrame方法会在系统每一次重画
GLSurfaceView时调用,默认频率为每秒60次。Cocos2dxRenderer类实现了GLSurfaceView.Renderer接口,并作为GLSurfaceView的渲染对象(具体参阅同目录下的Cocos2dxActivity.java文件中init方法的代码),系统自然就会以每秒60次的频率调用该onDrawFrame方法。
需要说明的是Cocos2dxRenderer类中sAnimationInterval是在我们调用Director::setAnimationInterval函数时,在Application::setAnimationInterval函数内,通过调用JNIHelper方法调用了Cocos2dxRender.setAnimationInterval方法设置进来,并被换算成以纳秒为单位的数值(1秒=1000000000纳秒)。
iOS版本
源代码:AppController.mm(位于PROJECT_DIR\proj.ios_mac\ios目录下)
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
cocos2d::Application *app = cocos2d::Application::getInstance();
cocos2d::Application *app = cocos2d::Application::getInstance();
... ...
app->run();
return YES;
}
源代码:CCApplication-ios.mm(位于PROJECT_DIR\cocos2d\cocos\platform\ios目录下)
int Application::run()
{
if (applicationDidFinishLaunching())
{
[[CCDirectorCaller sharedDirectorCaller] startMainLoop];
}
return 0;
}
void Application::setAnimationInterval(double interval)
{
[[CCDirectorCaller sharedDirectorCaller] setAnimationInterval: interval ];
}
{
if (applicationDidFinishLaunching())
{
[[CCDirectorCaller sharedDirectorCaller] startMainLoop];
}
return 0;
}
void Application::setAnimationInterval(double interval)
{
[[CCDirectorCaller sharedDirectorCaller] setAnimationInterval: interval ];
}
void Application::setAnimationInterval(double interval)
{
[[CCDirectorCaller sharedDirectorCaller] setAnimationInterval: interval ];
}
{
[[CCDirectorCaller sharedDirectorCaller] setAnimationInterval: interval ];
}
源代码:CCDirectorCaller-ios.mm(位于PROJECT_DIR\cocos2d\cocos\platform\ios目录下)
-(void) startMainLoop
{
// Director::setAnimationInterval() is called,we should invalidate it first
[self stopMainLoop];
displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(doCaller:)];
[displayLink setFrameInterval: self.interval];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
-(void) stopMainLoop
{
[displayLink invalidate];
displayLink = nil;
}
-(void) setAnimationInterval:(double)intervalNew
{
// Director::setAnimationInterval() is called,we should invalidate it first
[self stopMainLoop];
self.interval = 60.0 * intervalNew;
displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(doCaller:)];
[displayLink setFrameInterval: self.interval];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
{
// Director::setAnimationInterval() is called,we should invalidate it first
[self stopMainLoop];
displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(doCaller:)];
[displayLink setFrameInterval: self.interval];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
-(void) stopMainLoop
{
[displayLink invalidate];
displayLink = nil;
}
-(void) setAnimationInterval:(double)intervalNew
{
// Director::setAnimationInterval() is called,we should invalidate it first
[self stopMainLoop];
self.interval = 60.0 * intervalNew;
displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(doCaller:)];
[displayLink setFrameInterval: self.interval];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
其中,CADisplayLink允许我们能定时刷新展示对象的绘制。
官方给出的解释为"A CADisplayLink object is a timer object that allows your application to synchronize its drawing to the refresh rate of the display.
Your application creates a new display link,providing a target object and a selector to be called when the screen is updated. Next,your application adds the display link to a run loop."