cocos2d动作讲解
从本章开始,我们开始讲解cocos2d-x库的动作(Action)。游戏的世界是一个动态的世界:无论是主角精灵还是NPC精灵都处于不断的运动当中,甚至是背景中漂流的树叶,随风而动的小草。这些明显的或者不明显的运动构成了我们栩栩如生的游戏世界。
仔细研究游戏中精灵的运动,我们发现:所有这样的运动都可以细分为若干个基本动作和基本动作的组合。通过进一步扩展,我们可以将同一精灵的更多动作和不同精灵之间的不同动作连贯起来,形成关于整个运动世界的连续模拟。
我们给出示例ZYG003,展示cocos2d-x支持的主要动作:
基本动作
从技术上来说,基本动作的本质就是改变某个图形对象的属性:位置、角度、大小等。cocos2d-x提供超过20种基本动作供我们使用。根据改变完 成所需的时间,可以分为瞬时动作和延时动作。其中,延时动作的执行速度又可以按照不同的方式来改变(位置、大小、颜色、闪烁……)。
再进一步介绍基本动作之前,我们先来简单明确一下动作是如何与CCNode关联起来的。CCNode有一个成员函数叫runAction,定义为:

/** Executes an action, and returns the action that is executed.
The node becomes the action's target.
@warning Starting from v0.8 actions don't retain their target anymore.
@since v0.7.1
@return An Action pointer
*/
CCAction* runAction(CCAction* action);

此接口确保所有的精灵都可以执行各种动作。也正是为了服从这个接口的定义,导致后续各种组合动作也都从CCAction派生。
下面的代码是通常调用某个动作的方法:

CCSize s = CCDirector::sharedDirector()->getWinSize();
// 创建动作
CCActionInterval *actionTo = CCMoveTo::actionWithDuration(2.0f, ccp(s.width - 40.0f, s.height - 40.0f));
// 使用动作(tamara是一个CCSprite指针)
tamara->runAction(actionTo);

瞬时动作
顾名思义,瞬时动作就是不需要时间,马上就完成的动作。瞬时动作的共同基类是CCActionInstant。
cocos2d-x提供以下瞬时动作:
瞬时动作大都有与之对应的属性设置方法,之所以作为一个动作来实现,是为了可以与其他动作形成一个连续动作。下面来看一下瞬时动作的使用。
放置 - CCPlace
效果类似于setPosition(ccp(x, y))。示例代码如下:

void InstantActionLayer::onPlace(CCObject* pSender)
{
CCSize s = CCDirector::sharedDirector()->getWinSize();
// 理论上使用伪随机数前应该srand一下,但是知易的例子中没有,不知道是不是故意这么设计的。
// 如果使用time(NULL)初始化伪随机数种子,要注意time_t有可能是64位的!
CCPoint p = ccp(CCRANDOM_0_1() * s.width, CCRANDOM_0_1() * s.height);
flight->runAction(CCPlace::actionWithPosition(p));
}

隐藏 - CCHide
效果类似于setIsVisible(false)。示例代码如下:
void InstantActionLayer::onHide(CCObject* pSender)
{
flight->runAction(CCHide::action());
}
显示 - CCShow
效果类似于setIsVisible(true)。示例代码如下:
void InstantActionLayer::onShow(CCObject* pSender)
{
flight->runAction(CCShow::action());
}
可见切换 - CCToggleVisibility
效果类似于setIsVisible(!getIsVisible())。示例代码如下:
void InstantActionLayer::onToggle(CCObject* pSender)
{
flight->runAction(CCToggleVisibility::action());
}
水平翻转 - CCFlipX
效果类似于setFlipX(true/false)。示例代码如下:
void InstantActionLayer::onFlipX(CCObject* pSender)
{
flight->runAction(CCFlipX::actionWithFlipX(true));
}
垂直翻转 - onFlipY
效果类似于setFlipY(true/false)。示例代码如下:
void InstantActionLayer::onFlipY(CCObject* pSender)
{
flight->runAction(CCFlipY::actionWithFlipY(true));
}
还有两个较为特殊的动作(网格重用 - CCReuseGrid|停止网格 - CCStopGrid),我们以后介绍。
延时动作
延时动作就是指动作的完成需要一段时间。因此,几乎所有的延时动作都使用执行时间作为第一个参数,它们有着共同的基类CCActionInterval。
cocos2d-x中常用的延时动作:
这里有一个简单的类命名规则:
CCXxxxTo - 绝对动作,执行的结果与当前的状态关系不密切;
CCXxxxBy - 相对动作,在当前的状态上执行某种动作,执行的结果与当前状态是紧密相关的。
移动到 - CCMoveTo
移动 - CCMoveBy
跳跃到 - CCJumpTo
参数为终点位置、跳跃高度和跳跃次数。
跳跃 - CCJumpBy
贝赛尔曲线 - CCBezierBy
支持三次贝赛尔曲线:P0-起点,P1-起点切线方向,P2-终点切线方向,P3-终点。
首先设置贝塞尔参数,然后执行。
放大到 - CCScaleTo
放大 - CCScaleBy
如果参数为小数,那就是缩小了。
旋转到 - CCRotateTo
旋转 - CCRotateBy
闪烁 - CCBlink
色调变化到 - CCTintTo
色调变换 - CCTintBy
变暗到 - CCFadeTo
由无变亮 - CCFadeIn
由亮变无 - CCFadeOut
组合动作
按照一定的次序将上述基本动作组合起来,形成连贯的一套组合动作。组合动作包括以下几类:
序列 - CCSequence
序列的使用非常简单,该类从CCActionInterval派生,本身就可以被CCNode对象执行。该类的作用就是线性排列若干个动作,然后按先后次序逐个执行。

void CompositionActionLayer::onSequence(CCObject* pSender)
{
CCSize s = CCDirector::sharedDirector()->getWinSize();
// 创建5个动作
CCFiniteTimeAction *action0 = CCPlace::actionWithPosition(ccp(s.width / , ));
CCFiniteTimeAction *action1 = CCMoveTo::actionWithDuration(1.2f, ccp(s.width - 50.0f, s.height - 50.0f));
CCFiniteTimeAction *action2 = CCJumpTo::actionWithDuration(1.2f, ccp(, ), 30.0f, );
CCFiniteTimeAction *action3 = CCBlink::actionWithDuration(1.2f, );
CCFiniteTimeAction *action4 = CCTintBy::actionWithDuration(0.5f, , , );
// 将5个动作组合为一个序列,注意不要忘了用NULL结尾。
flight->runAction(CCSequence::actions(action0, action1, action2, action3, action4, action0, NULL));
}

同步 - CCSpawn
同步的使用非常简单,该类也是从CCActionInterval派生,可以被CCNode对象执行。该类的作用就是同时并列执行若干个动作,但要求动作本身是可以同时执行的,比如:移动式翻转、变色、缩放等。
需要特别注意的是,同步执行最后完成的时间由基本动作中用时最大者决定。

void CompositionActionLayer::onSpawn(CCObject* pSender)
{
CCSize s = CCDirector::sharedDirector()->getWinSize();
flight->setRotation(0.0f);
flight->setPosition(ccp(s.width / , ));
// 创建4个需要并行的动作,确保动作用时可组合。(action1/action2/sequence的执行时间都是2秒)
CCFiniteTimeAction *action1 = CCMoveTo::actionWithDuration(2.0f, ccp(s.width - 50.0f, s.height - 50.0f));
CCFiniteTimeAction *action2 = CCRotateTo::actionWithDuration(2.0f, 180.0f);
CCFiniteTimeAction *action3 = CCScaleTo::actionWithDuration(1.0f, 4.0f);
CCFiniteTimeAction *action4 = CCScaleBy::actionWithDuration(1.0f, 0.5f);
CCFiniteTimeAction *sequence = CCSequence::actions(action3, action4, NULL);
// 创建并执行同步动作。
flight->runAction(CCSpawn::actions(action1, action2, sequence, NULL));
}

重复有限次数 - CCRepeat
CCRepeat用来将某一动作重复有限次数,示例代码如下:

void CompositionActionLayer::onRepeat(CCObject* pSender)
{
CCSize s = CCDirector::sharedDirector()->getWinSize();
flight->setRotation(0.0f);
flight->setPosition(ccp(s.width / , ));
// 创建动作序列
CCFiniteTimeAction *action1 = CCMoveTo::actionWithDuration(2.0f, ccp(s.width - 50.0f, s.height - 50.0f));
CCFiniteTimeAction *action2 = CCJumpBy::actionWithDuration(2.0f, ccp(-, -), 30.0f, );
CCFiniteTimeAction *action3 = CCJumpBy::actionWithDuration(2.0f, ccp(s.width / , ), 20.0f, );
CCFiniteTimeAction *sequence = CCSequence::actions(action1, action2, action3, NULL);
// 重复运行上述动作序列3次
flight->runAction(CCRepeat::actionWithAction(sequence, ));
}

反动作 - Reverse
反动作就是反向(逆向)执行某个动作,支持针对动作序列的反动作序列。反动作不是一个专门的类,而是CCFiniteTimeAction引入的一个接口。不是所有的类都支持反动作,CCXxxxTo类通常不支持反动作,而CCXxxxBy类通常支持,示例如下:

void CompositionActionLayer::onReverse(CCObject* pSender)
{
CCSize s = CCDirector::sharedDirector()->getWinSize();
flight->setRotation(0.0f);
flight->setPosition(ccp(s.width / , )); CCFiniteTimeAction *action1 = CCMoveBy::actionWithDuration(2.0f, ccp(, ));
// 创建某个动作的反动作
CCFiniteTimeAction *action2 = action1->reverse(); flight->runAction(CCRepeat::actionWithAction(CCSequence::actions(action1, action2, NULL), ));
}

动画 - CCAnimate
动画就是让精灵自身连续执行一段影像,形成模拟运动的效果:行走时的状态,打斗时的状态等。

void CompositionActionLayer::onAnimation(CCObject* pSender)
{
CCSpriteBatchNode *mgr = static_cast<CCSpriteBatchNode *>(this->getChildByTag()); CCAnimation *animation = CCAnimation::animation();
animation->setName("flight");
animation->setDelay(0.2f);
for (int i = ; i < ; ++i)
{
// 定义每一帧的内容
float x = static_cast<float>(i % );
animation->addFrameWithTexture(mgr->getTexture(), CCRectMake(x * , , , ));
}
// 创建并执行动画效果,而且要重复10次。
CCAnimate *action = CCAnimate::actionWithAnimation(animation);
flight->runAction(CCRepeat::actionWithAction(action, ));
}

无限重复 - CCRepeatForever
在CCRepeatForever Class Reference中, 有这样一条警告“Warning: This action can't be Sequenceable because it is not an IntervalAction”,而实际上它的确是派生自CCActionInterval,这真有点儿把我也搞懵了。
仅从其本意来说,该类的作用就是无限期执行某个动作或动作序列,直到被停止。因此无法参与序列和同步,自身也无法反向执行(但是你可以将某一动作反向,然后无限重复执行)。

void CompositionActionLayer::onRepeatForever(CCObject* pSender)
{
CCSpriteBatchNode *mgr = static_cast<CCSpriteBatchNode *>(this->getChildByTag());
// 飞行喷火模拟动画
CCAnimation *animation = CCAnimation::animation();
animation->setName("flight");
animation->setDelay(0.1f);
for (int i = ; i < ; ++i)
{
float x = static_cast<float>(i % );
animation->addFrameWithTexture(mgr->getTexture(), CCRectMake(x * , , , ));
}
CCAnimate *action = CCAnimate::actionWithAnimation(animation);
// 将该动画作为精灵的本征动画,一直运行。
flight->runAction(CCRepeatForever::actionWithAction(action)); CCSize s = CCDirector::sharedDirector()->getWinSize();
flight->setRotation(0.0f);
flight->setPosition(ccp(, )); // 创建第二个连续无限期动作序列,叠加两者形成完整效果。
ccBezierConfig bezier;
bezier.controlPoint_1 = ccp(, s.height / );
bezier.controlPoint_2 = ccp(, -s.height / );
bezier.endPosition = ccp(, );
CCFiniteTimeAction *action1 = CCBezierBy::actionWithDuration(3.0f, bezier);
CCFiniteTimeAction *action2 = CCTintBy::actionWithDuration(0.5f, , , );
CCFiniteTimeAction *action3 = CCSpawn::actions(action1, CCRepeat::actionWithAction(action2, ), NULL);
CCFiniteTimeAction *action4 = CCSpawn::actions(action1->reverse(), CCRepeat::actionWithAction(action2, ), NULL);
// CCSequence的actions成员函数返回的是CCFiniteTimeAction指针类型,
// 而CCRepeatForever的actionWithAction接受的是CCActionInterval指针类型,
// 所以这里需要强转一下,转成CCSequence指针类型,
// 只要保证序列中有2个或2个以上的动作,这么做是绝对没有问题的。
flight->runAction(CCRepeatForever::actionWithAction(static_cast<CCSequence *>(CCSequence::actions(action3, action4, NULL))));
}

速度变化
基本动作和组合动作实现了针对精灵的各种运动和动画效果,但它们的速度通常是恒定不变的。通过CCActionEase类系和CCSpeed类,我们可以很方便地改变精灵执行动作的速度,是由快至慢还是由慢至快。
CCEaseIn - 由慢至快(速度线性变化)
CCEaseOut - 由快至慢
CCEaseInOut - 由慢至快再由快至慢
CCEaseSineIn - 由慢至快(速度正弦变化)
CCEaseSineOut - 由快至慢
CCEaseSineInOut - 由慢至快再由快至慢
CCEaseExponentialIn - 由慢至极快(速度指数级变化)
CCEaseExponentialOut - 由极快至慢
CCEaseExponentialInOut - 由慢至极快再由极快至慢
CCSpeed - 人工设定速度,还可通过setSpeed不断调整。
扩展动作
我们已经掌握了各种各样的动作,也可以按照不同的速度要求修改动作执行的时间,cocos2d-x还提供了针对现有动作的扩展,以实现各种灵活的效果。
延时 - CCDelayTime
通过CCDelayTime,我们可以在动作序列中增加一个时间间歇。

void ExtendActionLayer::onDelay(CCObject* pSender)
{
CCFiniteTimeAction *action1 = CCMoveBy::actionWithDuration(1.2f, ccp(, ));
CCFiniteTimeAction *action2 = action1->reverse();
// 实现一个等待间歇
flight->runAction(CCSequence::actions(action1, CCDelayTime::actionWithDuration(1.0f), action2, NULL));
}

函数调用
在动作序列中间或者末尾调用某个函数,执行任何任务:动作、状态修改等。在cocos2d-x中,调用函数的动作一共有4种。
1.CCCallFunc
仅函数调用,无任何参数。

void ExtendActionLayer::onCallBack1()
{
flight->runAction(CCTintBy::actionWithDuration(0.5f, , , ));
} void ExtendActionLayer::onCallFunc(CCObject* pSender)
{
CCFiniteTimeAction *action1 = CCMoveBy::actionWithDuration(2.0f, ccp(, ));
CCFiniteTimeAction *action2 = action1->reverse();
CCFiniteTimeAction *actionF = CCCallFunc::actionWithTarget(this, callfunc_selector(ExtendActionLayer::onCallBack1));
flight->runAction(CCSequence::actions(action1, actionF, action2, NULL));
}

2.CCCallFuncN
调用函数,并将当前对象的指针(CCNode指针)作为第一个参数传递进去。

void ExtendActionLayer::onCallBack2(CCNode* pSender)
{
// 在这个例子里,pSender就是flight,因为是他执行了那个actionF
pSender->runAction(CCTintBy::actionWithDuration(1.0f, , , ));
} void ExtendActionLayer::onCallFuncN(CCObject* pSender)
{
CCFiniteTimeAction *action1 = CCMoveBy::actionWithDuration(2.0f, ccp(, ));
CCFiniteTimeAction *action2 = action1->reverse();
CCFiniteTimeAction *actionF = CCCallFuncN::actionWithTarget(this, callfuncN_selector(ExtendActionLayer::onCallBack2));
flight->runAction(CCSequence::actions(action1, actionF, action2, NULL));
}

3.CCCallFuncND
在前一种方式的基础上增加一个数据参数,这是void指针类型。

void ExtendActionLayer::onCallBack3(CCNode* pSender, void* pData)
{
pSender->runAction(CCTintBy::actionWithDuration(static_cast<float>((int)pData), , , ));
} void ExtendActionLayer::onCallFuncND(CCObject* pSender)
{
CCFiniteTimeAction *action1 = CCMoveBy::actionWithDuration(2.0f, ccp(, ));
CCFiniteTimeAction *action2 = action1->reverse();
// 这里直接将整数常量强转成(void *)类型似乎有欠妥当,但对这些Action的生命周期不太清楚,稍后深入一下。
CCFiniteTimeAction *actionF = CCCallFuncND::actionWithTarget(this, callfuncND_selector(ExtendActionLayer::onCallBack3), (void *));
flight->runAction(CCSequence::actions(action1, actionF, action2, NULL));
}

4.CCCallFuncO
调用函数,并传递一个CCObject指针作为参数。这个似乎不太常用,资料比较少,以后再深入。
小结
至此,我们对cocos2d-x支持的动作有了整体了解。动作是我们的好帮手,它让游戏世界充满生机。在后面的章节中,我们会对部分动作以及动作系统继续深入。
cocos2d动作讲解的更多相关文章
- cocos2d动作
1.动作的基类是CCAction,通过继承它可以实现很多不同的动作,主要分为三大类: (1)CCFiniteTimeAction(有限次动作执行类) (2)CCSpeed(节点执行速度类) (3)CC ...
- Cocos2d-x视频教程
目录 1. 我的技术专栏 2. 相关推荐 3. 下载链接 4. cocos2d-xx Lua+JS+C++教学视频 5. 杨丰盛Cocos2D-X游戏课程 6. [Cocos2d-x]塔防游戏开发实战 ...
- Cocos2d-x 3.x游戏开发之旅
Cocos2d-x 3.x游戏开发之旅 钟迪龙 著 ISBN 978-7-121-24276-2 2014年10月出版 定价:79.00元 516页 16开 内容提要 <Cocos2d-x ...
- cocos2D v3.x中动作回调函数的变化
cocos2D v3.x版本中的动作的回调函数不能再带任何参数并且不能返回任何值. 官方给出的传递参数的办法是: 选择器(selector)不能带有任何形参,选择器需要的参数必须通过ivar或prop ...
- [一位菜鸟的COCOS-2D编程之路]COCOS2D中得动作,特效和动画
一,CCActionManager 管理所有节点动作的对象 来看看打飞机里面的一个onEnter 方法 - (void)onEnter { [super onEnter]; //一定要注意添加此方法, ...
- 【Cocos2d-X开发学习笔记】第12期:动作类CCAction的详细讲解
一般对于游戏中的精灵而言,它们不仅仅是存在于场景中,而且是动态展现的,例如,精灵移动的动态效果.动 画效果.跳动效果.闪烁和旋转动态效果等.每一种效果都可以看成是精灵的一个动作. 一.动作类(CCAc ...
- cocos2d中box2d讲解一
在游戏中我们经常要加入物理碰撞等和物理有关的内容,在游戏中加入物理引擎可以使我们的游戏更加真实,为玩家展示一个更真实的世界,cocos2d-x支持两个物理引擎Box2d和Chipmunk,本文介绍bo ...
- 小尝试一下 cocos2d
好奇 cocos2d 到底是怎样一个框架,正好有个项目需要一个游戏框架,所以稍微了解了一下.小结一下了解到的情况. 基本概念 首先呢,因为 cocos2d 是基于 pyglet 做的,你完全可以直接用 ...
- 实例讲解 SQL 注入攻击
这是一篇讲解SQL注入的实例文章,一步一步跟着作者脚步探索如何注入成功,展现了一次完整的渗透流程,值得一读.翻译水平有限,见谅! 一位客户让我们针对只有他们企业员工和顾客能使用的企业内网进行渗透测试. ...
随机推荐
- jenkins tags
List Subversion tags (and more) 参数设置 Tags filter ^((?!_ta_).)*$ 表示不含_ta_ Tags filtertrunk|(tags|bran ...
- 初探JavaScript魅力(三)
复选框的全选.反选和不选 <title>无标题文档</title> <style> body{background:#666;} </style> &l ...
- elasticsearch使用jetty进行简单的权限控制
默认elasticsearch是使用netty作为http的容器,由于netty并没有权限模块,所以默认es没有任何的权限控制,直接通过http就可以进行任何操作,除非把http禁用.但如果你使用el ...
- 一个很好的通用 excel 导出工具类
此类用主要 jxl +注解+流 实现扩展性很强,jxl性能会比poi好一点,值得我们学习. package oa.common.utils; import java.io.OutputStream; ...
- HDU - 1702 ACboy needs your help again!(栈和队列)
Description ACboy was kidnapped!! he miss his mother very much and is very scare now.You can't image ...
- GoF 设计模式:浅浅印象
23种设计模式,常常多个模式结合使用,主要是为了解决中大型软件项目"类和对象"膨胀的问题,进而有效组织类的结构而提出的.可划分为3类:创建型(关于类的创建),结构型(多个类的组织) ...
- MyEclipse报错 Building workspace has encountered a problem Errors occurred during the build 的2种解决方法
1: Building workspace has encountered a problem Errors occurred during the build 如果报错这个 那么有可能是jar包,报 ...
- android脚步---UI界面修改,关于activity中增加按钮和监听
增加按钮和监听,这个和上个不同在于,它不是在一个dialog里面,而是从新写了一个activity,因此需要先找到这个activity的入口. case R.id.checkframe: if (mC ...
- 模仿 app
原文链接:http://www.jianshu.com/p/a634b66cb180 前言 作为一个IOS程序员,闲的时候也想自己做一个app练练手,又苦于没有UI设计,也没有好的idea,所以只能先 ...
- HTML5的兼容问题以及调用js文件的方法
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...