Cocos2dx Screen Log
还是先上一段demo,看看美不美
正如视频显示,log是打在手机屏幕上的,介绍是来自上面的链接,将log打在屏幕上更为直观,即使用户没有一个可以呈现输出的开发环境,你也可以通过这种方式进行测试。根据原文所说,在ios和Android上进行过测试
可以work在Cocos2dx 3.x版本上
重点来了, 使用方法:
添加下面两个文件
ScreenLog.h
ScreenLog.cpp
在AppDelegate类中,include ScreenLog.h,并在ScreenLog类开始处初始化它的的global实例,可以在构造器中添加下面代码:
AppDelegate::AppDelegate() {
g_screenLog = new ScreenLog();
g_screenLog->setLevelMask( LL_DEBUG | LL_INFO | LL_WARNING | LL_ERROR | LL_FATAL );
g_screenLog->setFontFile( "UbuntuMono-R.ttf" );
g_screenLog->setTimeoutSeconds( 15 );
}
上面的levelMask是用来指定输出log信息的类型的bitmask, 根据你的需要对其进行开关设置, logging levels可以任意设置,当然也可以根据你需要进行随意改变
#define LL_FATAL 0x01
#define LL_ERROR 0x02
#define LL_WARNING 0x04
#define LL_INFO 0x08
#define LL_DEBUG 0x10
#define LL_TRACE 0x20
通常开发期间都会使用五个levels,偶尔需要定位程序时,也会使用trace levels,通常用来打印函数的输入输出(见下面的tips),
你所使用的devices可能没必要设置字体, 如果你想设置non-ascii字符,你就需要一个支持它的字符集.
timeout指定了log信息消失之前可以被显示多久.
- 在AppDelegate::applicationDidFinishLaunching() 方法的末端,附加screenlog到starting scene.
bool AppDelegate::applicationDidFinishLaunching() {
---blah blah blah ---
// create a scene. its an autorelease object
auto scene = HelloWorld::scene();
// attach screenlog to the scene about to become active
g_screenLog->attachToScene( scene );
// run
director->runWithScene( scene );
return true;
}
attachToScene方法让screenlog将它自己以子节点的形式(ScreenLog是cocos2d::Layer的子类)添加到给定的scene,它有可能比其他scene中任何layer都高.默认的child level是1000, 如果你想更改,可以在ScreenLog.cpp中,修改SCREENLOG_LAYER_LEVEL的值,来保证screenlog layer是top-most,保持在最上层.
现在,你可以开始在屏幕上打印输出log信息了,include ScreenLog.h并调用log方法,e.g.
g_screenLog->log( LL_INFO, "Touch began: (%d,%d)", x, y );
输出字符串的格式和printf一样.
下面是一个打印log信息的例子,添加下面的code到init()方法中:
g_screenLog->log( LL_DEBUG, "A debug message... フォント" );
g_screenLog->log( LL_INFO, "An info message... によって" );
g_screenLog->log( LL_WARNING, "A warning message... 日本語も" );
g_screenLog->log( LL_ERROR, "An error message... 表示可能" );
在许多设备上,不需要setFontFile方法的调用,就可以显示日文.
Note: 记住, 当切换scene时, 要记住attach它到要切的新的scene中.
auto scene = MyNextScreen::scene();
g_screenLog->attachToScene( scene ); // important
CCDirector::sharedDirector()->replaceScene( scene );
如果忘记添加它,screenlog会继续exist并run,但你看不到它.
动态文本信息
在存在的信息中,改变文本信息,也就是想输出动态文本信息,类似Cocos2dx的TextAlta类, 能够更快的更新,例如打印输出随着平移触摸而更新的触控位置
在比如,我想update一个文件下载的进度信息,当我首次创建screenLogMessage的引用,我可以保持这个log信息的引用:
screenLogMessage* slm = g_screenLog->log( LL_INFO, "Downloading file: 0% complete" );
…以后使用它来改变同样log信息的文本,而并非重新添加
g_screenLog->setMessageText( slm, "Downloading file: %d% complete", progress );
当一条log信息update如上, timeout计数也会复位,也就是说,如果这条更新的log信息timeout, 同样会从screenlog中remove掉.
多线程问题(这段还在研究)
在ScreenLog.cpp中,你可以看到一些code处含有threads和locking,看起来似乎没必要.创建的理由是仅仅只有application的主线程能够正确创建用来log打印显示的CCLabelTTF labels,如果其他线程来做创建这件事,labels将无法被创建,因为OpenGL渲染环境并非当前的线程(原文是说because the OpenGL rendering context is not current for that thread).为了允许其他线程随时调用‘log’,除非application的主线程可以处理时, 进程实际的label-creation部分才被缓冲.
原文这段是:
In ScreenLog.cpp you will see a bunch of code to do with threads and locking which might seem unnecessary at first. The reason for that is because only the main thread of the application is able to correctly create the CCLabelTTF labels used for the log display. If any other thread tries this, the labels will not be created because the OpenGL rendering context is not current for that thread. To allow any other thread to call 'log' at any time, the actual label-creation part of the process is buffered until the main thread of the application can do it. This allows logging from other threads in situations where processing is being done concurrently, such as long loading routines, or network transfers as mentioned above.
If you didn't understand that previous paragraph, just know that you can call 'log' at any time from anywhere in your code without worrying about threading issues :)
其他使用tips
头文件里,你能够发现一个比较小的类, 可以用来log程序的过程信息
class ScopeLog {
public:
std::string m_functionName;
ScopeLog(std::string fn) {
m_functionName = fn;
g_screenLog->log( LL_TRACE, "Entered %s", m_functionName.c_str() );
}
~ScopeLog() {
g_screenLog->log( LL_TRACE, "Exiting %s", m_functionName.c_str() );
}
};
这使用了栈中一个类实例的scope timing来打印输出两条log信息.例如如果我们在scene()的开始和init()方法里添加一行code如下:
Scene* HelloWorld::scene()
{
ScopeLog log(__PRETTY_FUNCTION__);
和
bool HelloWorld::init()
{
ScopeLog log(__PRETTY_FUNCTION__);
我们将能得到如下结果,说明了init()怎样出发在scene()函数内: