首页 > 代码库 > cocos2d-x学习篇之计时器

cocos2d-x学习篇之计时器

Cocos2d-x学习篇之简单的时钟

由于C++很菜,在做这个小工具时遇到各种各样蛋疼的问题,甚至准备放弃了,不过,最后还是搞出来了,真尼玛不容易啊。

刚开始想,显示本地时间不是很容易吗,取计算机的当前时间拼接成字符串显示在CCLabelTTF的实例上不就行了吗,几分钟的事,结果整整搞了一天。

首先,cocos2d-x不支持汉字显示,也就是不支持Unicode编码,从网上找了方法来转换一下,如下:

#ifndef CHINESE_H

#define CHINESE_H

#define _GLIBCXX_USE_WSTRING

 

#include "iostream"

#include "string"

 

using namespace std;

 

class Chinese

{

   //! convert from wstring to UTF8 using self-coding-converting

public:

   inline void WStrToUTF8(std::string& dest, const wstring& src){

 

      dest.clear();

 

      for (size_t i = 0; i < src.size(); i++){

 

         wchar_t w = src[i];

 

         if (w <= 0x7f)

 

            dest.push_back((char)w);

 

         else if (w <= 0x7ff){

 

            dest.push_back(0xc0 | ((w >> 6)& 0x1f));

 

            dest.push_back(0x80| (w & 0x3f));

 

         }

 

         else if (w <= 0xffff){

 

            dest.push_back(0xe0 | ((w >> 12)& 0x0f));

 

            dest.push_back(0x80| ((w >> 6) & 0x3f));

 

            dest.push_back(0x80| (w & 0x3f));

 

         }

 

         else if (sizeof(wchar_t) > 2 && w <= 0x10ffff){

 

            dest.push_back(0xf0 | ((w >> 18)& 0x07)); // wchar_t 4-bytes situation

 

            dest.push_back(0x80| ((w >> 12) & 0x3f));

 

            dest.push_back(0x80| ((w >> 6) & 0x3f));

 

            dest.push_back(0x80| (w & 0x3f));

 

         }

 

         else

 

            dest.push_back(‘?‘);

 

      }

 

   }

 

   //! simple warpper

 

   inline std::string chineseStr(const std::wstring& str){

 

      std::string result;

 

      WStrToUTF8(result, str);

 

      return result;

 

   }

 

   BOOL StringToWString(const std::string &str,std::wstring &wstr)

   {   

      int nLen = (int)str.length();   

      wstr.resize(nLen,L‘ ‘);

     

      int nResult = MultiByteToWideChar(CP_ACP,0,(LPCSTR)str.c_str(),nLen,(LPWSTR)wstr.c_str(),nLen);

     

      if (nResult == 0)

      {

          return FALSE;

      }

       

      return TRUE;

   }

 //wstring高?字Á?节¨²不?为a0,ê?返¤¦Ì回?FALSE

   BOOL WStringToString(const std::wstring &wstr,std::string &str)

   {   

      int nLen = (int)wstr.length();   

      str.resize(nLen,‘ ‘);

     

      int nResult = WideCharToMultiByte(CP_ACP,0,(LPCWSTR)wstr.c_str(),nLen,(LPSTR)str.c_str(),nLen,NULL,NULL);

     

      if (nResult == 0)

      {

         return FALSE;

      }

      return TRUE;

   }

};

 

#endif

接下来就是要显示了,首先说一下自己的错误思路,在文本框中显示并更新字符串,用个死循环每隔1s刷新时间就好了,所以写了个死循环放在HelloWorldScene.cpp中的init()方法中,运行下看看,结果尼玛,坑爹啊,界面都不出来了,看了下main中的代码,感觉可能是先执行死循环在执行run()方法的,就显示不出来,好吧,放在main中不就行了,不过这样的话显示的方法showInfo()就需要一个参数了,对,就是那个CCLabelTTF的实例,所以就写了下面的代码:

void showInfo(CCLabelTTF* label)

   {

      time_t t;

      time(&t);

      while(true)

      {

         Sleep(1000);

         tm* t_tm = localtime(&t);

         char year[6];

         _itoa(t_tm->tm_year + 1900, year, 10);

         char month[4];

         _itoa(t_tm->tm_mon + 1, month, 10);

         char day[6];

         _itoa(t_tm->tm_mday, day, 10);

         char hour[4];

         _itoa(t_tm->tm_hour, hour, 10);

         char minuter[4];

         _itoa(t_tm->tm_min, minuter, 10);

         char second[4];

         _itoa(t_tm->tm_sec, second, 10);

         Chinese ch;

         wstring w_str;

         ch.StringToWString(string(year) + "年" + string(month) + "月" + string(day) + "日" + string(hour) + "时" + string(minuter) + "分" + string(second) + "秒", w_str);

         string result = ch.chineseStr(w_str);

         label->setString(result.c_str());

      }

   }

不过这个方法要初始化一下啊,不然时间往哪显示去,好吧,在HelloWorldScene.cpp中定义显示的文本框是初始化下,结果尼玛,毫无疑问的又出现问题了,为什么?showInfo()中有死循环,一初始化就执行死循环了。

后来想了想,应该先让界面运行出来了再执行死循环,因此改了下main.cpp的代码,又想显示代码的文本应该是唯一的,不然可能会出现问题,so弄个单例来单独处理显示:

#ifndef _TIME_SHOW

#define _TIME_SHOW

 

#include "AppDelegate.h"

#include "CCEGLView.h"

#include "windows.h"

#include "Chinese.h"

 

#pragma once

USING_NS_CC;

 

class TimeShow

{

public:

   ~TimeShow(void);

 

   static TimeShow* getInstance()

   {

      if(_instance == NULL)

      {

         _instance = new TimeShow();

      }

      return _instance;

   }

 

   void showInfo()

   {

      time_t t;

      time(&t);

      while(true)

      {

         Sleep(1000);

         tm* t_tm = localtime(&t);

         char year[6];

         _itoa(t_tm->tm_year + 1900, year, 10);

         char month[4];

         _itoa(t_tm->tm_mon + 1, month, 10);

         char day[6];

         _itoa(t_tm->tm_mday, day, 10);

         char hour[4];

         _itoa(t_tm->tm_hour, hour, 10);

         char minuter[4];

         _itoa(t_tm->tm_min, minuter, 10);

         char second[4];

         _itoa(t_tm->tm_sec, second, 10);

         Chinese ch;

         wstring w_str;

         ch.StringToWString(string(year) + "年" + string(month) + "月" + string(day) + "日" + string(hour) + "时" + string(minuter) + "分" + string(second) + "秒", w_str);

         string result = ch.chineseStr(w_str);

         label->setString(result.c_str());

      }

   }

   void setLabel(CCLabelTTF* _label)

   {

      label = _label;

   }

 

private:

   CCLabelTTF* label;

   static TimeShow* _instance;

   TimeShow(void);

};

 

#endif

 

 

这样,在初始化文本框后调用下setLabel()即可,真是黄金大脑啊。

OK,运行一下,尼玛就出现这个问题:

 

什么原因呢?可能是单例的写法有问题,网上查了下,没错啊,就是这么写的,又查了下报的这个错误,有人说静态变量应该在头文件中初始化下,好吧,在#endif前加上一句TimeShow* TimeShow::_instance = NULL;

F5一下,尼玛又报错:

 

坑爹啊,又上网查找,半天没找到原因,想想以前学C++的时候,初始化都是放在cpp文件中的,将声明剪切到TimeShow.cpp中:

#include "TimeShow.h"

 

TimeShow* TimeShow::_instance = NULL;

 

TimeShow::TimeShow(void)

{

}

 

 

TimeShow::~TimeShow(void)

{

}

 

F5下,哇,终于没有上面的错误了,尼玛瞬间泪流满面,感慨万分。可是0.01s后,又悲剧了,没有编译错误,运行错误出来了:

没事,万事有google,搜搜更健康,我擦,网上的说法五花八门,想想,可能是setLabel的时候传过来的那个指针有问题,没办法了,先看看别人怎么实现的吧,搜索下cocos2d-x显示本地时间,尼玛,自带三种定时器,卧槽,我简直是自虐啊,完全找死的节奏,人家都写好了,自己还傻不拉几的去用死循环。

好吧,不作死就不会死,研究下API,管它那个好呢,先实现再说,

this->scheduleUpdate();

这个简单,没有参数,只需要在头文件中定义update()方法然后实现就OK了,

virtual void update(float data);

为什么用virtual?其实我也不知道,我只知道virturl声明的方法在类的继承和多态中会调用相应子类的对应方法,我这又没继承。Update()的实现如下:

void HelloWorld::update(float data)

{

   //CCLog("update");

   time_t t;

   time(&t);

   tm* t_tm = localtime(&t);

   char year[6];

   _itoa(t_tm->tm_year + 1900, year, 10);

   char month[4];

   _itoa(t_tm->tm_mon + 1, month, 10);

   char day[6];

   _itoa(t_tm->tm_mday, day, 10);

   char hour[4];

   _itoa(t_tm->tm_hour, hour, 10);

   char minuter[4];

   _itoa(t_tm->tm_min, minuter, 10);

   char second[4];

   _itoa(t_tm->tm_sec, second, 10);

   Chinese ch;

   wstring w_str;

   ch.StringToWString(string(year) + "年¨º" + string(month) + "月?" + string(day) + "日¨?" + string(hour) + "时º¡À" + string(minuter) + "分¤?" + string(second) + "秒?", w_str);

   string result = ch.chineseStr(w_str);

   label->setString(result.c_str());

}

 

其实这个很简单了,就是取本地时间转化为字符串显示在文本框上,这个label是哪的呢?刚开始想肯定需要个全局变量,放在.h文件中可能好点,

CCLabelTTF* label;

发现又报错:

 

结果只好放在.cpp中了,为什么会这样,我不会,坐等大神解释。

      F5下,苍天啊大地啊,终于见到想要的结果了,55555~~~~

      最后觉得scheduleUpdate()是每帧执行一次的话可能太浪费了,改成每秒:

this->schedule(schedule_selector(HelloWorld::update), 1.0);

最后的效果如下:

 

 

附:完整的HelloWorldScene.cpp的代码如下:

 

#include "HelloWorldScene.h"

#include "string"

#include "Chinese.h"

#include "windows.h"

#include "TimeShow.h"

 

USING_NS_CC;

using namespace std;

 

CCLabelTTF* label;

 

 

CCScene* HelloWorld::scene()

{

    // ‘scene‘ is an autorelease object

    CCScene *scene = CCScene::create();

   

    // ‘layer‘ is an autorelease object

    HelloWorld *layer = HelloWorld::create();

 

    // add layer as a child to scene

    scene->addChild(layer);

 

    // return the scene

    return scene;

}

 

// on "init" you need to initialize your instance

bool HelloWorld::init()

{

    //////////////////////////////

    // 1. super init first

    if ( !CCLayer::init() )

    {

        return false;

    }

   

    CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();

    CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();

 

    /////////////////////////////

    // 2. add a menu item with "X" image, which is clicked to quit the program

    //    you may modify it.

 

    // add a "close" icon to exit the progress. it‘s an autorelease object

    CCMenuItemImage *pCloseItem = CCMenuItemImage::create(

                                        "CloseNormal.png",

                                        "CloseSelected.png",

                                        this,

                                        menu_selector(HelloWorld::menuCloseCallback));

   

   pCloseItem->setPosition(ccp(origin.x + visibleSize.width - pCloseItem->getContentSize().width/2 ,

                                origin.y + pCloseItem->getContentSize().height/2));

 

    // create menu, it‘s an autorelease object

    CCMenu* pMenu = CCMenu::create(pCloseItem, NULL);

    pMenu->setPosition(CCPointZero);

    this->addChild(pMenu, 1);

 

    /////////////////////////////

    // 3. add your codes below...

 

    // add a label shows "Hello World"

    // create and initialize a label

   

    CCLabelTTF* pLabel = CCLabelTTF::create("Hello World", "Arial", 24);

   CCLabelTTF* pLabel_0 = CCLabelTTF::create("Hello Baby", "Courier New", 18);

   CCNode* node = CCNode::create();

   this->addChild(node, 0);

   //node->convertToNodeSpace()

   

    // position the label on the center of the screen

    pLabel->setPosition(ccp(origin.x + visibleSize.width/2,

                            origin.y + visibleSize.height - pLabel->getContentSize().height));

   pLabel_0->setPosition(ccp(origin.x + visibleSize.width/2 + 30, origin.y + visibleSize.height - pLabel_0->getContentSize().height));

   label = pLabel_0;

    // add the label as a child to this layer

   // this->addChild(pLabel, 1);

   //TimeShow::getInstance()->setLabel(pLabel_0);

 

    // add "HelloWorld" splash screen"

    CCSprite* pSprite = CCSprite::create("HelloWorld.png");

 

   CCSprite* pGirl = CCSprite::create("girl.jpg");

   node->addChild(pGirl, 0);

   pGirl->setPosition(ccp(visibleSize.width / 2 + origin.x, visibleSize.height / 2 + origin.y));

 

    // position the sprite on the center of the screen

    pSprite->setPosition(ccp(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

 

   //pGirl->setPosition(ccp(0, 0));

 

   node->addChild(pLabel_0, 1);

    // add the sprite as a child to this layer

    //this->addChild(pSprite, 0);

   //this->scheduleUpdate();

   this->schedule(schedule_selector(HelloWorld::update), 1.0);

   //TimeShow::getInstance()->showInfo();

    return true;

}

 

void HelloWorld::update(float data)

{

   //CCLog("update");

   time_t t;

   time(&t);

   tm* t_tm = localtime(&t);

   char year[6];

   _itoa(t_tm->tm_year + 1900, year, 10);

   char month[4];

   _itoa(t_tm->tm_mon + 1, month, 10);

   char day[6];

   _itoa(t_tm->tm_mday, day, 10);

   char hour[4];

   _itoa(t_tm->tm_hour, hour, 10);

   char minuter[4];

   _itoa(t_tm->tm_min, minuter, 10);

   char second[4];

   _itoa(t_tm->tm_sec, second, 10);

   Chinese ch;

   wstring w_str;

   ch.StringToWString(string(year) + "年¨º" + string(month) + "月?" + string(day) + "日¨?" + string(hour) + "时º¡À" + string(minuter) + "分¤?" + string(second) + "秒?", w_str);

   string result = ch.chineseStr(w_str);

   label->setString(result.c_str());

}

 

void onEnter()

{

 

}

 

 

void HelloWorld::menuCloseCallback(CCObject* pSender)

{

#if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) || (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)

   CCMessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert");

#else

    CCDirector::sharedDirector()->end();

#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)

    exit(0);

#endif

#endif

}

 

 

唉!路漫漫其修远兮,吾将上下而求索!

cocos2d-x学习篇之计时器