cocos2dx学习之路----第十二篇(关于Node节点的自我更新函数Update探讨及定时器的使用)

这一篇来谈谈关于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及定时器的使用就将到这里,下一篇就来说说关于动作类的相关的。有了它,又为场景中的对象增添了不少“光彩”哈~



你可能感兴趣的:(cocos2dx学习之路----第十二篇(关于Node节点的自我更新函数Update探讨及定时器的使用))