Cocos2dx学习笔记(24)——动作类CCAction

Cocos中定义了一系列基本动作,可以方便我们制作简单的效果。

本文仅供个人记录和复习,不用于其他用途

3.x版本变化

  • 去掉CC
  • 更新了一些动作
  • CallFunc有了新用法,具体请看回调函数一章

CCAction

CCAction只是一个基类,它的派生类才是我们实际使用的动作。派生类如下:

  • CCFiniteTimeAction:与时间相关的动作类
  • CCFollow:跟随动作类
  • CCSpeed:速度类

动作管理

下面是一些常见操作,适用于所有的动作:

1
2
3
4
5
6
sp->runAction("action对象"); // 执行
sp->pauseSchedulerAndActions(); // 暂停
sp->resumeSchedulerAndActions(); // 继续
sp->stopAction("action对象"); // 停止对象的某个action动作
sp->stopActionByTag("tag值"); // 停止对象的tag动作
sp->stopAllActions(); // 停止对象的所有动作

下面将分别介绍三个类各自的操作。

时间动作类CCFiniteTimeAction

CCFiniteTimeAction又分为CCActionInstant(瞬时动作)和CCActionInterval(持续动作)两个子类。

瞬时动作CCActionInstant

常见操作如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 翻转
CCFlipX::create('是否左右翻转bool');
CCFlipY::create('是否上下翻转bool');
// 设置坐标
CCPlace::create('位置CCPoint');
// 显示、隐藏
// 即:setVisible(),来设置可见/不可见
CCShow::create();
CCHide::create();
// 可见切换
// 即可见变不可见,不可见变可见。
CCToggleVisibility::create();

使用举例:

1
2
3
4
// 瞬间翻转Y的动作
CCSprite* sp = CCSprite::create("Icon.png");
CCFlipY* flipY = CCFlipY::create(true);
sp->runAction(flipY);

函数回调动作CCCallFunc

CCCallFunc是瞬时动作CCActionInstant的子类。它主要有三种函数回调动作类。三种函数回调的区别在于所带的参数个数:0、1、2。CCActionInstant在执行动作时,会调用这个函数。

1
2
3
CCCallFunc::create('对象this', '回调函数'); // 回调函数:不带参数
CCCallFuncN::create('对象this', '回调函数'); // 回调函数:传递着本身作为参数(CCNode* node)
CCCallFuncND::create('对象this', '回调函数', '任意参数void'); // 回调函数:带2个参数(CCNode* node,void* a)

使用举例:

1
2
3
4
5
6
7
8
9
// 定义函数回调动作
CCCallFunc* back1 = CCCallFunc::create(this, callfunc_selector(MyLayer::funback1));
CCCallFuncN* back2 = CCCallFuncN::create(this, callfuncN_selector(MyLayer::funback2));
CCCallFuncND* back3 = CCCallFuncND::create(this, callfuncND_selector(MyLayer::funback3),(void *)10);
// 回调函数
void MyLayer::funback1() { ... } // CCCallFunc回调函数
void MyLayer::funback2(CCNode* node) { ... } // CCCallFuncN回调函数
void MyLayer::funback3(CCNode* node,void* a) { ... } // CCCallFuncND回调函数

持续动作CCActionInterval

常见操作如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// To:绝对动作
// By:相对动作
// 如果当前对象的坐标为(20, 20)
// CCMoveTo(50, 50)后,移动到了(50, 50)的位置
// CCMoveBy(50, 50)后,则是相对当前坐标进行移动,最后坐标为(70, 70)
// 移动相关
// 几秒后移动到坐标点
CCMoveTo::create("时间","坐标");
CCMoveBy::create("时间","坐标");
// 几秒后经过几次弹跳到指定位置
CCJumpTo::create("时间","目标位置","高度","到目标所需次数");
CCJumpBy::create("时间","目标位置","高度","到目标所需次数");
// 几秒内按指定贝塞尔曲线运动
CCBezierTo::create("时间","ccBezierConfig构造体");
CCBezierBy::create("时间","ccBezierConfig构造体");
// 几秒内按曲线运动(拟合度0最柔和)
CCCardinalSplineTo::create("时间","控制点坐标数组","拟合度");
CCCardinalSplineBy::create("时间","控制点坐标数组","拟合度");
// 几秒内完成一个样条插值轨迹(直线)
CCCatmullRomTo::create("时间","控制点坐标数组");
CCCatmullRomBy::create("时间","控制点坐标数组");
// 几秒内球面运动
CCOrbitCamera::create("时间","起始半径","半径差","起始z角","旋转z角","起始x角","旋转x角");
// 缩放相关
// 几秒后缩放到指定大小(1:原大小;大于1:放大;小于1:缩小)
CCScaleTo::create("时间","缩放比例");
CCScaleBy::create("时间","缩放比例");
// 旋转相关
// 几秒后旋转多少度,单位:角度
CCRotateTo::create("时间","角度");
CCRotateBy::create("时间","角度");
// 倾斜相关
// 几秒后倾斜指定角度,单位:角度
CCSkewTo::create("时间","x轴角度","y轴角度");
CCSkewBy::create("时间","x轴角度","y轴角度");
// 颜色相关
// 几秒后变为指定RGB颜色,颜色取值[0,255]
CCTintTo::create("时间","红","绿","蓝");
CCTintBy::create("时间","红","绿","蓝");
// 设置透明度[0,255](255为不透明)
CCFadeIn::create("时间"); //淡入,透明度从0到255
CCFadeOut::create("时间"); //淡出,透明度从255到0
CCFadeTo::create(1.0f,80); //透明度转化为指定值
// 几秒内闪烁几次
CCBlink::create("时间","次数");

使用举例:

1
2
3
4
// 1.0秒内移动到(200,200)
CCSprite* sp = CCSprite::create("Icon.png");
CCMoveTo* moveTo = CCMoveTo::create(1.0f, ccp(200, 200));
sp->runAction(moveTo);

组合动作

组合动作的类是CCActionInterval的子类,主要分为序列动作和重复动作。

1
2
3
4
5
6
7
// 序列动作
CCSpawn::create("action对象1", "action对象2", ..., NULL); // 动作同时执行
CCSequence::create("action对象1", "action对象2", ..., NULL); // 动作按顺序执行
// 重复动作
CCRepeat::create("action对象","次数"); // 重复次数
CCRepeatForever::create("action对象"); // 无限重复

使用举例:

1
2
3
4
5
6
7
8
9
// 每次移动后闪烁3次,重复无限次。
CCSprite* sp = CCSprite::create("Icon.png");
CCMoveBy* move = CCMoveBy::create(1.0f, ccp(10, 10));
CCBlink* blink = CCBlink::create(1.0f, 3);
CCSequence* seq = CCSequence::create(move, blink, NULL);
CCRepeatForever* repeatForever = CCRepeatForever::create(seq);
sp->runAction(repeatForever);

延迟动作CCDelayTime

CCDelayTimeCCActionInterval的子类,用于序列CCSequence中,在两个动作之间加上一个等待时间。

1
2
3
4
5
6
7
// 移动后,等待3秒,再执行闪烁动作
CCSprite* sp = CCSprite::create("Icon.png");
CCMoveBy* moveBy = CCMoveBy::create(2.0f, ccp(300, 300));
CCBlink* blink = CCBlink::create(2.0f, 10);
CCDelayTime* delay = CCDelayTime::create(3.0);
CCSequence* seq = CCSequence::create(moveBy, delay, blink, NULL);
sp->runAction(seq);

缓动动作CCActionEase

CCActionEase也是CCActionInterval的子类,也是缓动动作的基类。这类动作在执行时速度可以变化,比如直线运动可以先加速再减速,又或者是自由落体运动是越来越快的,但是我们用的更多的是EaseRateAction类,该类继承了CCActionEase

缓动动作的速度可以分为以下三种:

  • In:由慢变快
  • Out:由快变慢
  • InOut:由慢变快再变慢

需要注意的是,CCActionEase只能够改变某个持续动作执行时的速度,但是这个动作的时间是不会变的。由于这个类的具体实现比较复杂,这里只简单地举几个例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 线性变化。
CCEaseIn::create("action对象", "加速率"); // 由慢到快
CCEaseOut::create("action对象", "加速率"); // 由快到慢
CCEaseInOut::create("action对象", "加速率"); // 由慢到快再到慢
// 正弦变化。
CCEaseSineIn::create("action对象"); // 由慢到快
CCEaseSineOut::create("action对象"); // 由快到慢
CCEaseSineInOut::create("action对象"); // 由慢到快再到慢
// 指数变化。速度的变化按指数增长。
CCEaseExponentialIn::create("action对象"); // 缓慢开始
CCEaseExponentialOut::create("action对象"); // 缓慢结束
CCEaseExponentialInOut::create("action对象"); // 缓慢开始并缓慢结束
// 反弹变化。类似球碰到地面,不断落下与反弹。
CCEaseBounceIn::create("action对象"); // 从起点反弹
CCEaseBounceOut::create("action对象"); // 从终点反弹
CCEaseBounceInOut::create("action对象"); // 起点终点都反弹
// 回力变化。类似拉弓、回力标。
CCEaseBackIn::create("action对象"); // 起点作为回力点
CCEaseBackOut::create("action对象"); // 终点作为回力点
CCEaseBackInOut::create("action对象"); // 起点终点都作为回力点
// 伸缩式变化。
CCEaseElasticIn::create("action对象"); // 起点具有弹性
CCEaseElasticOut::create("action对象"); // 终点具有弹性
CCEaseElasticInOut::create("action对象"); // 起点终点都具有弹性

速度类CCSpeed

CCSpeed继承自CCAction,用于改变动作执行的速度。但不同于CCActionEase,动作执行的时间也会随着改变,也相当于一般意义上的加速。

1
2
3
4
5
// 以5倍速度执行moveBy,执行完动作只需1.0秒。
CCSprite* sp = CCSprite::create("Icon.png");
CCMoveBy* moveBy = CCMoveBy::create(5.0f, ccp(300, 300));
CCSpeed* speed = CCSpeed::create(moveBy, 5.0);
sp->runAction(speed);

跟随动作类CCFollow

CCFollow继承自CCAction,它能够让某个CCNode对象跟随另外一个对象移动。就好比我们在玩超级马里奥时,我们控制人物向前走,背景也会跟着显示前面的内容,而不是让我们的马里奥走到屏幕外面去。

使用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 获得屏幕尺寸
CCSize mysize = CCDirector::sharedDirector()->getVisibleSize();
// background.jpg尺寸678*331。作为参照物
CCSprite* bg = CCSprite::create("sprite/background.jpg");
bg->setPosition(ccp(0, 0));
bg->setAnchorPoint(ccp(0, 0));
this->addChild(bg);
// 创建精灵sp
CCSprite* sp = CCSprite::create("sprite/plant.png");
sp->setPosition(ccp(0, 160));
this->addChild(sp);
// sp执行移动动作,5秒移动到(678,160)
CCMoveTo* moveTo = CCMoveTo::create(5.0f, ccp(678, 160));
sp->runAction(moveTo);
// 图层this跟随sp移动,跟随范围678*331
CCFollow* follow = CCFollow::create(sp, CCRectMake(0, 0, 678, 331));
this->runAction(follow);

综合运用

下面将结合几种常用动作,以及碰撞检测的知识,来做一个小的样例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
bool T15Speed::init()
{
if (!CCLayer::init())
{
return false;
}
hero = CCSprite::create("animation/hero.png");
hero->setPosition(ccp(100, 160));
addChild(hero);
by = CCMoveBy::create(10, ccp(300, 0));
food = CCSprite::create("animation/food.png");
food->setPosition(ccp(250, 160));
addChild(food);
CCSpeed *speed = CCSpeed::create(by, 2);
hero->runAction(speed);
CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("animation/run.plist");
CCAnimation *animation = CCAnimation::create();
char buffer[100];
for (int i = 0; i != 15; i++)
{
memset(buffer, 0, sizeof(buffer));
sprintf(buffer, "run%d.png", i + 1);
animation->addSpriteFrame(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(buffer));
}
animation->setDelayPerUnit(0.1f);
animation->setLoops(-1);
CCAnimate *animate = CCAnimate::create(animation);
hero->runAction(animate);
scheduleUpdate();
return true;
}
void T15Speed::update(float dt)
{
CCRect heroRect = CCRect(
hero->boundingBox().origin.x + 50,
hero->boundingBox().origin.y,
hero->boundingBox().size.width - 100,
hero->boundingBox().size.height
);
if (food && heroRect.intersectsRect(food->boundingBox()))
{
food->removeFromParentAndCleanup(true);
food = NULL;
CCSpeed *speed = CCSpeed::create(by, 5);
hero->runAction(speed);
}
}

上面是init()方法以及定时器的update()方法的实现。这里我们创建了两个精灵,一个是英雄,一个是仙豆。那么,我们先让英雄以较慢的速度向右移动,当触碰到线仙豆时,英雄的速度加快。关于碰撞检测的实现,请看下面这张图:

由于英雄的boundingBox()过大,所以我们只能够自定义一个矩形。origin代表着hero的boundingBox的当前坐标(Rect左上角为原点),origin.x加50代表着矩形向右移动了50。当然,宽度我们必须减去100,不然的话就会提前碰到敌人。注意,size.width在减时是两边同时减,在这里就是左右两边同时减50。当然,由于矩形锚点在左上角,所以看起来只是右边减去了100。

效果如下:

我们会发现一点,hero不仅执行了动作,同时还执行了动画。runAction()可以同时运行动作和动画,但是不能同时运行两个动作或者两个动画。