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

时间: 2024-08-14 09:51:43

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

vue学习指南:第十二篇(详细) - Vue的 路由 第二篇 ( 路由按需加载(懒加载))

各位朋友 因 最近工作繁忙,小编停更了一段时间,快过年了,小编祝愿 大家 事业有成 学业有成 快乐健康 2020开心过好每一天.从今天开始 我会抽时间把 Vue 的知识点补充完整,以及后期会带给大家更完善的知识体系!!! 路由的按需加载(懒加载) 我们都知道 vue的路由是我们必学的,也是不可分离的一部分,我们传统引入路由的方式是这样的, import Home from '@/components/home/Home' 但是在真正项目的开发中,这种现象是杜绝的,是不建议使用这种传统方式引入的.

Spark学习之路 (十二)SparkCore的调优之资源调优

摘抄自:https://tech.meituan.com/spark-tuning-basic.html 一.概述 在开发完Spark作业之后,就该为作业配置合适的资源了.Spark的资源参数,基本都可以在spark-submit命令中作为参数设置.很多Spark初学者,通常不知道该设置哪些必要的参数,以及如何设置这些参数,最后就只能胡乱设置,甚至压根儿不设置.资源参数设置的不合理,可能会导致没有充分利用集群资源,作业运行会极其缓慢:或者设置的资源过大,队列没有足够的资源来提供,进而导致各种异常

萌新的Linux学习之路(十二)---软件安装

一.软件名称识别 [abrt-addon-ccpp]-[2.1.11-19].[e17].[x86_64].rpm   ##rpm结尾的适用于redhat操作系统 软件名称      软件版本 适用系统 64位 二.如何安装软件 1.rpm rpm     -ivh       ##安装 -v显示过程,-h指定加密方式为hash -e         ##卸载 -ql        ##查询软件生成文件 -qlp        ##查询软件安装后会生成什么文件 -qa        ## 查新系

python学习之路(十二)

这节主要介绍一下import!很实用的调用模块的功能. 导入模块 是导入真实的代码 而导入包 是导入包下面的 __init__() 文件 这两个是不一样的 先说模块定义 模块 它就是一个 py 文件 它就是负责从逻辑上 组织代码的 代码里面 它可以有变量 有函数 有类 有逻辑 模块 它不是这些东西的简单堆砌 而是要实现功能 创建模块 就是 new 一个 python file 定义一个模块: 在另一个文件中 要使用这个模块 所以要这样来调用: 模块名 点 变量名 模块名 点 方法名 import

《C++primer(第五版)》学习之路-第十二章:动态内存

[ 声明:版权所有,转载请标明出处,请勿用于商业用途.  联系信箱:[email protected]] 12.1 动态内存与智能指针 1.在C++中,动态内存的管理是通过一对运算符来完成:new,在动态内存中为对象分配空间并返回一个指向该对象的指针,我们可以选择对对象进行初始化:delete,接受一个动态对象的指针,销毁该对象,并释放与之关联的内存. 2. shared_ptr允许多个指针指向同一个对象:unique_ptr则"独占"所指向的对象.标准库还定义了一个名为weak_pt

WPF学习之路(十二)控件(Content控件)

Label Label相比TextBlock功能并不强大,但是支持键盘快捷键的方式获得焦点 <StackPanel> <Label Target="{Binding ElementName=txtA}">Choose _A</Label> <TextBox Name="txtA"></TextBox> <Label Target="{Binding ElementName=txtB}&quo

WPF学习之路(十二)控件(HeaderedContent控件)

GroupBox 用来组织多种控件的常见控件,因为是内容空间,只能直接包含一项,需要使用面板一类的中间空间. Header和Content可以是任意元素 <GroupBox> <GroupBox.Header> <WrapPanel Margin="5"> <Image Source=".\Image\icon.png"></Image> <TextBlock Text="Database

Hive学习之路 (十二)Hive SQL练习之影评案例

案例说明 现有如此三份数据:1.users.dat 数据格式为: 2::M::56::16::70072, 共有6040条数据对应字段为:UserID BigInt, Gender String, Age Int, Occupation String, Zipcode String对应字段中文解释:用户id,性别,年龄,职业,邮政编码 2.movies.dat 数据格式为: 2::Jumanji (1995)::Adventure|Children's|Fantasy, 共有3883条数据对应字

JAVA基础学习之路(十二)链表

定义链表的基本结构: class Link {//外部类 //内部类,只为链表类服务 private class Node {//定义节点类 private String data;//保存的数据 private Node next;//引用关系 public Node (String data) { this.data = data; } private Node root;//定义根节点 } } 1.数据增加 public void add(数据类型,变量) 如果要向链表之中增加数据,应该由