Cocos2d-x学习笔记(六)CCAction分析

原创文章,转载请注明出处:http://blog.csdn.net/sfh366958228/article/details/38821319

前言

千呼万唤始出来,不知你与我的心情是否一样,终于是等到了CCAction的出场。如果说CCSprite是身体,那么CCAction一定就是灵魂,它的组合,让整个游戏充满活力,当然,充满活力的方法也并非仅此而已。

源码分析

class CC_DLL CCAction : public CCObject
{
public:
    CCAction(void);
    virtual ~CCAction(void);
    const char* description();

    //
    virtual CCObject* copyWithZone(CCZone *pZone);

    // 动作是否完成
    virtual bool isDone(void);

    // 在动作开始之前被调用,它将设置一个target
    virtual void startWithTarget(CCNode *pTarget);

    // 在动作完成后调用,它将把target设置为空。注意,不要调用这个方法,取而代之的十target->stopAction(action)
    virtual void stop(void);

    // 调用每一帧。注意:不要重构它,除非你确保自己在做什么
    virtual void step(float dt);

    /**
     *每帧会调用一次,time是一个0~1之间的值
     *
     * For example:
     * - 0 意味着动作刚开始
     * - 0.5 意味着动作处于中间
     * - 1 意味着动作结束了
     **/
    virtual void update(float time);

    // 获取/设置动作的target
    inline CCNode* getTarget(void) { return m_pTarget; }
    inline void setTarget(CCNode *pTarget) { m_pTarget = pTarget; }

    // 获取/设置动作最初的target(一般不调用set)
    inline CCNode* getOriginalTarget(void) { return m_pOriginalTarget; }
    inline void setOriginalTarget(CCNode *pOriginalTarget) { m_pOriginalTarget = pOriginalTarget; }

    // 获取/设置Tag
    inline int getTag(void) { return m_nTag; }
    inline void setTag(int nTag) { m_nTag = nTag; }

public:
    // 创建一个Action
    static CCAction* create();
protected:
    CCNode    *m_pOriginalTarget;
    CCNode    *m_pTarget;
    int     m_nTag;
};

CCNode子类层级分析

CCAction是整个动作体系的基类,在CCNode上使用runAction即可执行,不仅可以完成一个独立的动作,也可以定义动作序列、组合动作、反向动作等。

这张图清晰的说明了整个CCAction的层级体系。主要分为三大类:CCFiniteTimeAction(有限次动作执行类)、CCSpeed(节点执行速度类)、CCFollow(节点跟随另一个节点移动)。

而CCFiniteTimeAction分为CCActionInstant(瞬时动作)和CCActionInterval(延迟动作)。我们看到的动作一般都是后者。

下面介绍一些常用的延时动作:

1)CCMoveTo / CCMoveBy 移动

2)CCRotateTo / CCRotateBy 旋转

3)CCSkewTo / CCSkewBy 扭曲

4)CCJumpTo / CCJumpBy 跳跃

5)CCBezierTo / CCBezierBy 贝塞尔曲线

6)CCBink 闪烁

7)CCScaleTo / CCScaleBy 缩放

8)CCFadeIn / CCFadeOut 淡入淡出

9)CCTintTo  / CCTintBy 染色

所有动作都是通过create方法来完成创建。例如:

CCMoveTo *move = CCMoveTo::create(2.0f, cpp(50, 50));

细心的话会发现很多方法都会有To和By两种,他们的区别是To是指定坐标,By是相对坐标。

CCActionManager

之前分析CCNode的时候有提到过CCActionManager,它是一个管理所有动作的单例,工作原理十:当CCNode执行runAction时,该函数会把动作通过动作管理类的addAction函数将对象传递给CCActionManager的单例该实例再把动作添加到自己的动作序列中。

一般情况下十不需要使用这个类的,我们完全可以使用CCNode类中的stopAction、StopActionByTag和stopAllAction等函数来管理,但是有两种特殊情况需要使用CCActionManager类单例:

1)动作的执行者不是同一个节点;

2)需要暂停、重启活动时。

class CC_DLL CCActionManager : public CCObject
{
public:
    CCActionManager(void);
    ~CCActionManager(void);

    // 为指定目标添加动作
    void addAction(CCAction *pAction, CCNode *pTarget, bool paused);

    // 删除所有动作
    void removeAllActions(void);

    // 删除制定目标所有动作
    void removeAllActionsFromTarget(CCObject *pTarget);

    // 删除指定动作
    void removeAction(CCAction *pAction);

    //根据标签删除/获取指定动作
    void removeActionByTag(unsigned int tag, CCObject *pTarget);
    CCAction* getActionByTag(unsigned int tag, CCObject *pTarget);

    // 获取目标的动作数
    unsigned int numberOfRunningActionsInTarget(CCObject *pTarget);

    // 暂停目标动作
    void pauseTarget(CCObject *pTarget);

    // 恢复目标动作
    void resumeTarget(CCObject *pTarget);

    // 暂停所有运行中的动作
    CCSet* pauseAllRunningActions();

    // 恢复指定集合所有动作
    void resumeTargets(CCSet *targetsToResume);

    ...
};

// 以上是CCActionManager源码分析
// 使用示例如下

pNode->runAction(CCScaleBy::create(2, 2));
CCDirector * pDirector = CCDirector::shareDirector();
pDirector->getActionManager()->pauseTarget(pNode);
pDirector->getActionManager()->resumeTarget(pNode);

注意:不要轻易使用动作管理类,除非是不同动作目标或者暂停重启动作。

CCRepeat和CCRepeatForever

CCRepeat和CCRepeatForever都是继承自CCActionInterval,可以实现动作的多次执行和重复执行。

// times是执行pAction的次数,而CCRepeat则是重复执行直到stop
static CCRepeat * create(CCFiniteTimeAction *pAction, unsigned int times);
static CCRepeatForever *create(CCActionInterval *pAction);

CCSequence和CCSpawn

CCSequence类可以实现按序列执行动作,让节点顺序执行这几个动作。

CCSpawn类实现同时执行几个动作,最终动作的持续时间由时间最长的动作决定。

例如:

CCSequence::create(move, move_back, NULL);// 记得在动作序列的结尾加上NULL

CCReverseTime

CCReverseTime就是反向执行某个动作,支持针对动作序列的反动作序列。不是所有的类都支持反动作,XxxTo通常不支持,XxxBy通常支持。

我们一般在做某个一个Action的时候,我们需要返回会用到该sprite的reverse,但是又时候,我需要这种行为再reverse的时候,我就可以利用CCReverseTime达到这种效果,例子代码如下:

CCSprite *spriteTint = CCSprite::create("blocks.png");
spriteTint->setPosition(ccp(size.width / 2.0f, size.height / 2.0f));
this->addChild(spriteTint, 1);

CCActionInterval *forwardBy = CCTintBy::create(4, 255, 0, 0);
CCActionInterval *back = forwardBy->reverse();
CCReverseTime *reverseTime = CCReverseTime::create(back); //在这里也就是倒序播放它的Action了
CCAction *action = CCSequence::create(forwardBy, back, NULL);
spriteTint->runAction(action);

CCSpeed和CCEaseAction

基本动作和组合动作实现了针对CCNode的各种属性改变,但这样改变的速度是不变的,通过CCEaseAction和CCSpeed类可以很方便的修改CCNode执行的速度。

CCSpeed也是一个包装器,可以改变内部动作执行时间。

CCSpeed *speed = CCSpeed::create(CCMoveBy::create(3, cpp(350, 0)), 2.0f);

CCEaseAction也是包装器,但是它一般用来让动作执行起来更加自然:

1)EaseIn // 由慢至快

2)EaseOut // 由快至慢

3)EaseInOut // 由慢至快,再由快至慢

4)EaseSineIn // 由慢至快

5)EaseSineOut // 由快至慢

6)EaseSineInOut // 由慢至快,再由快至慢

7)EaseExponentialIn // 由慢至极快

8)EaseExponentialOut // 由极快至慢

9)EaseExponentialInOut // 由慢至极快,再由极快至慢

具体的差异可以通过调试查看。

CCFollow

CCFollow可以让一个节点跟随另一个节点作位移。

static CCFollow *create(CCNode *pFollowNode, const CCRect &rect = CCRectZero);

通过该方法来创建一个CCFollow跟随动作,可以设置一个跟随范围,离开范围就不再跟随。

CCFollow经常用来设置Layer跟随sprite,可以实现类似摄像机的跟拍效果,TestCpp中也有类似的例子。

void ActionFollow::onEnter()
{
    ActionsDemo::onEnter();

    centerSprites(1);
    CCSize s = CCDirector::sharedDirector()->getWinSize();

    m_grossini->setPosition(ccp(-200, s.height / 2));
    CCActionInterval* move      = CCMoveBy::create(2, ccp(s.width * 3, 0));
    CCActionInterval* move_back = move->reverse();
    CCSequence* seq       = CCSequence::create(move, move_back, NULL);
    CCAction* rep               = CCRepeatForever::create(seq);

    m_grossini->runAction(rep);

    this->runAction(CCFollow::create(m_grossini, CCRectMake(0, 0, s.width * 2 - 100, s.height)));
}

组合技

无论是CCRepeat、CCRepeatForever、CCSequence、CCSpawen、CCSpeed、CCEaseAction,都是包容器,包含一个或多个动作,组成一个特定的动作,如下,我们将模拟一个心脏跳动的动画。

CCSprite* pSprite = CCSprite::create("Heart.png");
this->addChild(pSprite, 0);
auto *scale = CCScaleBy::create(1.0, 1.5);
auto *scaleBack = scale->reverse();
auto *scaleForever = CCRepeatForever::create(CCSequence::create(scale, scaleBack, NULL));
pSprite->runAction(scaleForever);
时间: 2024-12-15 19:09:04

Cocos2d-x学习笔记(六)CCAction分析的相关文章

python之raw_input()(学习笔记六)

python之raw_input()(学习笔记六) 我们经常使用raw_input()读取用户的输入,如下例子所示: >>> name = raw_input('please input your name:'),截图如下: 下面简单说下,raw_input()与if搭配使用,脚本如下: #!/usr/bin/env python # -*- coding:utf-8 -*- birth = raw_input('birth:') if birth < 2000: print '0

cocos2dx游戏开发学习笔记3-lua面向对象分析

在lua中,可以通过元表来实现类.对象.继承等.与元表相关的方法有setmetatable().__index.getmetatable().__newindex. 具体什么是元表在这里就不细说了,网上很多介绍,这里主要讲与cocos2dx相关联的部分. 在lua-binding库中extern.lua里,有如下方法: --Create an class. function class(classname, super) local superType = type(super) local c

Time, Delays, and Deferred Work &lt;LDD3&gt; 学习笔记 + jiffies.h 分析

Time, Delays, and Deferred Work Dealing with time involves the following tasks, in order of increasing complexity: ? Measuring time lapses and comparing times ? Knowing the current time ? Delaying operation for a specified amount of time ? Scheduling

swift学习笔记(六)析构过程和使用闭包对属性进行默认值赋值

一.通过闭包和函数实现属性的默认值 当某个存储属性的默认值需要定制时,可以通过闭包或全局函数来为其提供定制的默认值. 注:全局函数结构体和枚举使用关键字static标注    函数则使用class关键字标注 当对一个属性使用闭包函数进行赋值时,每当此属性所述的类型被创建实例时,对应的闭包或函数会被调用,而他们的返回值会被作为属性的默认值. ESC: Class SomeCLass{ let someProperty:SomeType={ //给someProperty赋一个默认值 //返回一个与

java之jvm学习笔记六-十二(实践写自己的安全管理器)(jar包的代码认证和签名) (实践对jar包的代码签名) (策略文件)(策略和保护域) (访问控制器) (访问控制器的栈校验机制) (jvm基本结构)

java之jvm学习笔记六(实践写自己的安全管理器) 安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用 AccessController的checkPerssiom方法,访问控制器AccessController的栈检查机制又遍历整个 PerssiomCollection来判断具体拥有什么权限一旦发现栈中一个权限不允许的时候抛出异常否则简单的返回,这个过程实际上比我的描述要复杂 得多,这里我只是简单的一句带过,因为这

初探swift语言的学习笔记六(ARC-自动引用计数,内存管理)

Swift使用自动引用计数(ARC)来管理应用程序的内存使用.这表示内存管理已经是Swift的一部分,在大多数情况下,你并不需要考虑内存的管理.当实例并不再被需要时,ARC会自动释放这些实例所使用的内存. 另外需要注意的: 引用计数仅仅作用于类实例上.结构和枚举是值类型,而非引用类型,所以不能被引用存储和传递. swift的ARC工作过程 每当创建一个类的实例,ARC分配一个内存块来存储这个实例的信息,包含了类型信息和实例的属性值信息. 另外当实例不再被使用时,ARC会释放实例所占用的内存,这些

Linux System Programming 学习笔记(六) 进程调度

1. 进程调度 the process scheduler is the component of a kernel that selects which process to run next. 进程调度器需要使 处理器使用率最大化,并且提供 使多个进程并发执行的虚拟 Deciding which processes run, when, and for how long is the process scheduler's fundamental responsibility. 时间片:th

Lua学习笔记(六):函数-续

Lua中的函数是带有词法定界(lexical scoping)的第一类值(first-class values).第一类值指:在Lua中函数和其他值(数值.字符串)一样,函数可以被存放在变量中,也可以存放在表中,可以作为函数的参数,还可以作为函数的返回值.词法定界指:嵌套的函数可以访问他外部函数中的变量.这一特性给Lua提供了强大的编程能力. Lua中关于函数稍微难以理解的是函数也可以没有名字,匿名的.当我们提到函数名(比如print),实际上是说一个指向函数的变量,像持有其他类型的变量一样:

laravel3学习笔记(六)

原作者博客:ieqi.net ==================================================================================================== ORM Laravel3中MVC体系中Model里最重要的组成部分无疑是ORM了,ORM — object-relational mapper — 将数据操作面向对象化,使得整个web框架的核心风格统一,降低整体复杂度,为开发者提供便利. Laravel3中的ORM叫

IBatis.Net学习笔记六--再谈查询

在IBatis.Net学习笔记五--常用的查询方式 中我提到了一些IBatis.Net中的查询,特别是配置文件的写法. 后来通过大家的讨论,特别是Anders Cui 的提醒,又发现了其他的多表查询的方式.在上一篇文章中我提到了三种方式,都是各有利弊:第一种方式当数据关联很多的情况下,实体类会很复杂:第二种方式比较灵活,但是不太符合OO的思想(不过,可以适当使用):第三种方式最主要的问题就是性能不太理想,配置比较麻烦. 下面是第四种多表查询的方式,相对第二种多了一点配置,但是其他方面都很好(当然