这一篇来谈谈关于Node节点比较重要的函数接口,自我更新接口(update)以及定时器。
好,下面先来谈谈节点的自我更新。
每一个Node节点都有自带一个update函数。而当调用ScheduleUpdate()这一方法便会被开启,在每一帧被调用。
我们可能注意到,在AppDelegate中有个setAnimationInterval()的方法,有导演类调用。而这个方法就是设置渲染的帧率的。默认是以60帧每秒进行渲染。当然,并不是真正的每秒就渲染60次,这个只能是差不多达到这个速率,因为程序在运行过程中会耗时,特别是执行一些比较耗时的方法。
那么,既然是这样,如果开启来我们更新函数update,那么程序就会以这个时间(1/60秒 = 0.01666秒)去调用每次的update函数,而这个时间便作为update的参数传入。
通过开启节点的update方法可以实现节点自己的逻辑代码的更新。
好,接着我们再说说定时器,如果要在cocos中使用定时器,需要用到Scheduler这一个类来使用,称为调度器。而这一个类的也被整理到Node中,然后提供我们使用它的方法。通过它的一系类方法可以实现我们所谓的定时器。我列出它其中一个开启定时器的方法,理解了这个,也就可以理解其它方法了。如下:
/**************************** **参数1:调度器调用的回调方法 **参数2:调度器每隔'interval‘秒调用一次 **参数3:调用(repeat+1)次 **参数4:第一次开始调用时隔’delay‘秒 ****************************** void Node::schedule(SEL_SCHEDULE selector, float interval, unsigned int repeat, float delay);
对于第二个参数,如果把时间设置为0,那么,将会像update一样每一帧被调用,不过如果这样,建议使用ScheduleUpdate方法调用自身的update函数。对于第三个参数,如果要一直调用,可以通过宏定义CC_REPEAT_FOREVER来设置。
好,那么我们现在就来看看这次的测试中如何来应用,先来看看运行结果:
上面一个节点是通过调用ScheduleUpdate方法来进行自我的位置更新,下面一个则是使用定时器的方法来进行更新。我们还可以看到有Start、Remove以及PauseCurrentTarget、ResumeCurrentTarget的菜单条目。它们是用来对调度器的操作的。需要提及的一点是,在调度器被开启的时候,我们可以通过获取当前被调度的函数来识别该调度器是否被开启,即通过调用IsSchedule这一方法。而对于函数自身节点的update是检测不到的自身的update只能是开启或者移除。这点需要注意一下。对于最下面的Pause和Resume可以对节点的所有调度器进行停止恢复。这也是很有用的一个方法。
好了,话不多说。来看看源码吧:
SchedulerTest.h:
#ifndef __SCHEDULER_TEST__ #define __SCHEDULER_TEST__ #include"cocos2d.h" USING_NS_CC; class SchedulerTest :public Scene{ public: static Scene *createScene(); virtual bool init(); CREATE_FUNC(SchedulerTest); void update(float dt); void Own_Update(float dt); //准备状态的函数 void ReadyFunc(float dt); private: Size visibleSize; }; #endif
SchedulerTest.cpp:
#include"SchedulerTest.h" Scene *SchedulerTest::createScene(){ auto scene = SchedulerTest::create(); return scene; } bool SchedulerTest::init(){ if (!Scene::init()){ return false; } visibleSize = Director::getInstance()->getVisibleSize(); //当前测试标签描述 auto test_label = Label::createWithSystemFont("About Node's Update Test", "", 30); test_label->setPosition(Vec2(visibleSize.width / 2, visibleSize.height - test_label->getContentSize().height)); this->addChild(test_label); /*************************************************************** **** 节点一 ****************************************************************/ LayerColor *block1 = LayerColor::create(Color4B::RED, 70, 70); this->addChild(block1); block1->ignoreAnchorPointForPosition(false); block1->setAnchorPoint(Vec2::ANCHOR_MIDDLE); block1->setPosition(visibleSize.width / 4, visibleSize.height * 3 / 4 - 50); //设置名字以便获取 block1->setName("block1"); //附加在节点一上的名字标签 auto label1 = Label::createWithSystemFont("block1", "", 22); block1->addChild(label1); label1->setPosition(block1->getContentSize() / 2); //节点一状态标签,对于状态,需要动态改变它,所以需要给它设置个名字 auto state_label1 = Label::createWithSystemFont("Block1 State:", "", 25); state_label1->setPosition(Vec2( state_label1->getContentSize().width / 2, block1->getPositionY() + block1->getContentSize().height) ); this->addChild(state_label1); auto state1 = Label::createWithSystemFont("Stop", "", 25); state1->setPosition(Vec2( visibleSize.width/2 , state_label1->getPositionY()) ); this->addChild(state1); //设置名字以便获取 state1->setName("state1"); /*************************************************************** **** 节点二 ****************************************************************/ LayerColor *block2 = LayerColor::create(Color4B::RED, 70, 70); this->addChild(block2); block2->ignoreAnchorPointForPosition(false); block2->setAnchorPoint(Vec2::ANCHOR_MIDDLE); block2->setPosition(Vec2(block1->getPositionX(), block1->getPositionY() - 160)); //设置名字以便获取 block2->setName("block2"); //附加在节点二上的名字标签 auto label2 = Label::createWithSystemFont("block2", "", 22); block2->addChild(label2); label2->setPosition(block2->getContentSize() / 2); //节点二状态标签 auto state_label2 = Label::createWithSystemFont("Block2 State:", "", 25); state_label2->setPosition(Vec2( state_label2->getContentSize().width / 2, block2->getPositionY() + block2->getContentSize().height) ); this->addChild(state_label2); auto state2 = Label::createWithSystemFont("Stop", "", 25); state2->setPosition(Vec2( visibleSize.width/2, state_label2->getPositionY()) ); this->addChild(state2); state2->setName("state2"); /*************************************************************** **** 菜单条目 ****************************************************************/ /***** **节点的Start菜单条目,用于节点开启调度器 ******/ MenuItemLabel* Start_menuItem1 = MenuItemLabel::create(Label::createWithSystemFont("Start", "", 25), [&](Ref* sender){ //修改Block1状态 auto state = dynamic_cast<Label*>(this->getChildByName("state1")); state->setString("Running..."); //开启当前节点的update this->scheduleUpdate(); //设置当前点击目标不可用 auto start_item = dynamic_cast<MenuItemLabel*>(sender); start_item->setEnabled(false); //获取Remove并设置为可用点击 auto menu = dynamic_cast<Menu*>(this->getChildByName("menu")); auto remove_item = dynamic_cast<MenuItemLabel*>(menu->getChildByName("remove_item1")); remove_item->setEnabled(true); }); Start_menuItem1->setPosition(Vec2( Start_menuItem1->getContentSize().width / 2 - visibleSize.width / 2, Start_menuItem1->getContentSize().height - 10) ); Start_menuItem1->setName("start_item1"); auto Start_menuItem2 = MenuItemLabel::create(Label::createWithSystemFont("Start", "", 25),[&](Ref *sender){ if (!isScheduled(CC_SCHEDULE_SELECTOR(SchedulerTest::Own_Update))){ //设置当前点击目标不可用 auto start_item = dynamic_cast<MenuItemLabel*>(sender); start_item->setEnabled(false); /***************************************** **开启自定义节点的update:在3秒之后开启,每1秒调用一次,一直开启 **参数相关: **param1 CC_SCHEDULE_SELECTOR(SchedulerTest::Own_Update):被调度器回调的函数 **param2 1.0f:每隔一秒调用一次 **param3 重复调用 **param4 第一次开启调度器延迟时间,这里为4秒 ******************************************/ this->schedule(CC_SCHEDULE_SELECTOR(SchedulerTest::Own_Update), 1.0f, CC_REPEAT_FOREVER, 4.0f); //开启另一个调度器,准备开启 this->schedule(CC_SCHEDULE_SELECTOR(SchedulerTest::ReadyFunc), 1.0f, 3, 0); } }); Start_menuItem2->setPosition(Vec2(Start_menuItem2->getContentSize().width / 2 - visibleSize.width / 2, -150)); Start_menuItem2->setName("start_item2"); /***** **节点的Remove菜单条目,用于移除节点调度器 ******/ MenuItemLabel *Remove_menuItem1 = MenuItemLabel::create(Label::createWithSystemFont("Remove", "", 25), [&](Ref *sender){ //修改Block1状态 auto state = dynamic_cast<Label*>(this->getChildByName("state1")); state->setString("Removed"); //关闭update this->unscheduleUpdate(); //设置当前目标不可用 auto pause_item = dynamic_cast<MenuItemLabel*>(sender); pause_item->setEnabled(false); //获取Start并设置为可点击 auto menu = dynamic_cast<Menu*>(this->getChildByName("menu")); auto start_item = dynamic_cast<MenuItemLabel*>(menu->getChildByName("start_item1")); start_item->setEnabled(true); }); Remove_menuItem1->setPosition(Vec2( Start_menuItem1->getPositionX() + Start_menuItem1->getContentSize().width * 2, Start_menuItem1->getPositionY())); Remove_menuItem1->setEnabled(false); Remove_menuItem1->setName("remove_item1"); auto Remove_menuItem2 = MenuItemLabel::create(Label::createWithSystemFont("Remove", "", 25), [&](Ref* sender){ if (isScheduled(CC_SCHEDULE_SELECTOR(SchedulerTest::Own_Update))){ //修改Block2状态 auto state = dynamic_cast<Label*>(this->getChildByName("state2")); state->setString("Removed"); this->unschedule(CC_SCHEDULE_SELECTOR(SchedulerTest::Own_Update)); //设置当前目标不可用 auto remove_item = dynamic_cast<MenuItemLabel*>(sender); remove_item->setEnabled(false); //获取Start并设置为可点击 auto menu = dynamic_cast<Menu*>(this->getChildByName("menu")); auto start_item = dynamic_cast<MenuItemLabel*>(menu->getChildByName("start_item2")); start_item->setEnabled(true); } }); Remove_menuItem2->setPosition(Vec2( Start_menuItem2->getPositionX() + Start_menuItem2->getContentSize().width * 2, Start_menuItem2->getPositionY())); Remove_menuItem2->setEnabled(false); Remove_menuItem2->setName("remove_item2"); /***** **节点的Pause菜单条目,用于暂停当前节点的所有调度器 ******/ auto Pause_menuItem = MenuItemLabel::create(Label::createWithSystemFont("PauseCurrentTarget", "", 25), [&](Ref* sender){ //暂停当前节点中的所有调度器 this->getScheduler()->pauseTarget(this); //设置当前目标不可用 auto remove_item = dynamic_cast<MenuItemLabel*>(sender); remove_item->setEnabled(false); //获取Resume并设置为可点击 auto menu = dynamic_cast<Menu*>(this->getChildByName("menu")); auto resume_item = dynamic_cast<MenuItemLabel*>(menu->getChildByName("resume_item")); resume_item->setEnabled(true); //修改Block1状态 auto state1 = dynamic_cast<Label*>(this->getChildByName("state1")); if (state1->getString() == "Running..."){ state1->setString("Pause"); } //修改Block2状态 auto state2 = dynamic_cast<Label*>(this->getChildByName("state2")); if (isScheduled(CC_SCHEDULE_SELECTOR(SchedulerTest::Own_Update)) || isScheduled(CC_SCHEDULE_SELECTOR(SchedulerTest::ReadyFunc))){ state2->setString("Pause"); } }); Pause_menuItem->setPosition(Vec2(0,Pause_menuItem->getContentSize().height * 3 - visibleSize.height/2)); Pause_menuItem->setName("pause_item"); /***** **节点的Resume菜单条目,用于恢复当前节点的所有调度器 ******/ auto Resume_menuItem = MenuItemLabel::create(Label::createWithSystemFont("ResumeCurrentTarget", "", 25), [&](Ref* sender){ //恢复当前节点的所有调度器 this->getScheduler()->resumeTarget(this); //设置当前目标不可用 auto remove_item = dynamic_cast<MenuItemLabel*>(sender); remove_item->setEnabled(false); //获取Pause并设置为可点击 auto menu = dynamic_cast<Menu*>(this->getChildByName("menu")); auto pause_item = dynamic_cast<MenuItemLabel*>(menu->getChildByName("pause_item")); pause_item->setEnabled(true); //修改Block1状态 auto state1 = dynamic_cast<Label*>(this->getChildByName("state1")); if (state1->getString() == "Pause"){ state1->setString("Running..."); } //修改Block2状态 auto state2 = dynamic_cast<Label*>(this->getChildByName("state2")); if (isScheduled(CC_SCHEDULE_SELECTOR(SchedulerTest::Own_Update))){ state2->setString("Running..."); } }); Resume_menuItem->setPosition(Vec2( Pause_menuItem->getPositionX(), Pause_menuItem->getPositionY() - Resume_menuItem->getContentSize().height * 2)); Resume_menuItem->setEnabled(false); Resume_menuItem->setName("resume_item"); //菜单 auto menu = Menu::create(Start_menuItem1, Start_menuItem2, Remove_menuItem1, Remove_menuItem2, Pause_menuItem, Resume_menuItem, NULL); menu->setName("menu"); this->addChild(menu); return true; } //节点的自我更新函数 void SchedulerTest::update(float dt){ //CCLOG("THE NODE'S UPDATE : %f...",dt); //判断节点的方向 static int dir1 = 1; //通过名字获取当前节点的子节点 auto block1 = dynamic_cast<LayerColor*>(this->getChildByName("block1")); Vec2 pos = Vec2(block1->getPositionX() + 3 * dir1, block1->getPositionY()); if (pos.x > visibleSize.width - block1->getContentSize().width/2 || pos.x < block1->getContentSize().width/2){ dir1 = -dir1; } block1->setPosition(pos); } //自己的自我更新函数 void SchedulerTest::Own_Update(float dt){ //log("This is My Own Update : %f...",dt); static int dir2 = 1; //通过名字获取当前节点的子节点 auto block2 = dynamic_cast<LayerColor*>(this->getChildByName("block2")); Vec2 pos = Vec2(block2->getPositionX() + 3 * dir2, block2->getPositionY()); if (pos.x > visibleSize.width - block2->getContentSize().width / 2 || pos.x < block2->getContentSize().width / 2){ dir2 = -dir2; } block2->setPosition(pos); } //准备状态的函数 void SchedulerTest::ReadyFunc(float dt){ static int i = 4; i--; //修改Block2状态 auto state = dynamic_cast<Label*>(this->getChildByName("state2")); state->setString(String::createWithFormat("Ready to Start:%d second", i)->getCString()); if (i == 0){ i = 4; //修改Block2状态 auto state = dynamic_cast<Label*>(this->getChildByName("state2")); state->setString("Running..."); //获取Remove. auto menu = dynamic_cast<Menu*>(this->getChildByName("menu")); auto remove_item = dynamic_cast<MenuItemLabel*>(menu->getChildByName("remove_item2")); remove_item->setEnabled(true); } }
其实获取节点的方法有多种,节点都有属于自己的tag和Name的,并且节点是通过添加和被添加的形式渲染在场景中,所以,一旦节点被渲染了,就可以有不同的方式获取。即可以通过tag或者Name来取得,前提是它的tag或是Name必须要被设置。
上面的代码我也做了比较详细的注释了,就不多说了,有不懂的也可以提出来哈~
好了,关于节点的自我更新函数Update及定时器的使用就将到这里,下一篇就来说说关于动作类的相关的。有了它,又为场景中的对象增添了不少“光彩”哈~