Extra2D/docs/API_Tutorial/09_Action_System.md

13 KiB
Raw Blame History

动作系统 (Action System)

Extra2D 的动作系统提供了一套完整的动画解决方案,参考 Cocos2d-x 的设计模式,支持丰富的动作类型和缓动效果。

目录

  1. 概述
  2. 核心概念
  3. 基本动作
  4. 组合动作
  5. 缓动动作
  6. 特殊动作
  7. 瞬时动作
  8. 动作管理
  9. 完整示例

概述

动作系统用于在节点上创建动画效果,通过修改节点的属性(位置、缩放、旋转、透明度等)实现各种动画。

类层次结构

Action (基类)
├── FiniteTimeAction (有限时间动作)
│   ├── ActionInterval (时间间隔动作)
│   │   ├── MoveBy/MoveTo
│   │   ├── JumpBy/JumpTo
│   │   ├── BezierBy/BezierTo
│   │   ├── ScaleBy/ScaleTo
│   │   ├── RotateBy/RotateTo
│   │   ├── FadeIn/FadeOut/FadeTo
│   │   ├── Blink
│   │   ├── TintBy/TintTo
│   │   ├── Sequence
│   │   ├── Spawn
│   │   ├── Repeat/RepeatForever
│   │   ├── DelayTime
│   │   ├── ReverseTime
│   │   └── ActionEase (缓动动作基类)
│   └── ActionInstant (瞬时动作)
│       ├── CallFunc/CallFuncN
│       ├── Place
│       ├── FlipX/FlipY
│       ├── Show/Hide
│       ├── ToggleVisibility
│       └── RemoveSelf
├── Speed (速度控制)
├── Follow (跟随动作)
└── TargetedAction (目标动作)

核心概念

创建动作

所有动作都使用静态工厂方法 create() 创建:

// 创建移动动作
auto move = MoveTo::create(2.0f, Vec2(100, 100));

// 创建缩放动作
auto scale = ScaleTo::create(1.0f, 2.0f);

// 创建旋转动作
auto rotate = RotateBy::create(3.0f, 360.0f);

运行动作

通过节点的 runAction() 方法运行动作:

sprite->runAction(move);

动作完成回调

使用 setCompletionCallback() 设置动作完成时的回调:

auto action = MoveTo::create(2.0f, Vec2(100, 100));
action->setCompletionCallback([]() {
    E2D_LOG_INFO("动作完成!");
});
sprite->runAction(action);

基本动作

移动动作

// MoveTo - 移动到指定位置
auto moveTo = MoveTo::create(2.0f, Vec2(100, 100));

// MoveBy - 相对移动
auto moveBy = MoveBy::create(2.0f, Vec2(50, 0));  // 向右移动 50 像素

跳跃动作

// JumpTo - 跳跃到指定位置
auto jumpTo = JumpTo::create(2.0f, Vec2(100, 100), 50, 3);  // 跳到 (100,100),高度 50跳 3 次

// JumpBy - 相对跳跃
auto jumpBy = JumpBy::create(2.0f, Vec2(100, 0), 50, 3);  // 向右跳跃 100 像素

贝塞尔曲线动作

BezierConfig bezier;
bezier.controlPoint1 = Vec2(100, 200);
bezier.controlPoint2 = Vec2(200, 100);
bezier.endPosition = Vec2(300, 150);

// BezierTo - 贝塞尔曲线移动到指定位置
auto bezierTo = BezierTo::create(3.0f, bezier);

// BezierBy - 相对贝塞尔曲线移动
auto bezierBy = BezierBy::create(3.0f, bezier);

缩放动作

// ScaleTo - 缩放到指定比例
auto scaleTo = ScaleTo::create(1.0f, 2.0f);        // 缩放到 2 倍
auto scaleToXY = ScaleTo::create(1.0f, 2.0f, 1.5f); // X 缩放 2 倍Y 缩放 1.5 倍

// ScaleBy - 相对缩放
auto scaleBy = ScaleBy::create(1.0f, 0.5f);  // 缩小一半

旋转动作

// RotateTo - 旋转到指定角度
auto rotateTo = RotateTo::create(2.0f, 90.0f);  // 旋转到 90 度

// RotateBy - 相对旋转
auto rotateBy = RotateBy::create(2.0f, 360.0f);  // 旋转 360 度

淡入淡出动作

// FadeIn - 淡入(透明度从 0 到 1
auto fadeIn = FadeIn::create(1.0f);

// FadeOut - 淡出(透明度从 1 到 0
auto fadeOut = FadeOut::create(1.0f);

// FadeTo - 淡入到指定透明度
auto fadeTo = FadeTo::create(1.0f, 0.5f);  // 淡入到 50% 透明度

闪烁动作

// Blink - 闪烁
auto blink = Blink::create(2.0f, 5);  // 2 秒内闪烁 5 次

色调动作

// TintTo - 变化到指定颜色
auto tintTo = TintTo::create(1.0f, 255, 0, 0);  // 变为红色

// TintBy - 相对颜色变化
auto tintBy = TintBy::create(1.0f, 50, 0, 0);  // 红色通道增加 50

组合动作

Sequence - 序列动作

按顺序依次执行多个动作:

auto sequence = Sequence::create(
    MoveTo::create(1.0f, Vec2(100, 100)),
    DelayTime::create(0.5f),
    FadeOut::create(1.0f),
    CallFunc::create([]() { E2D_LOG_INFO("完成"); }),
    nullptr  // 必须以 nullptr 结尾
);
sprite->runAction(sequence);

Spawn - 并行动作

同时执行多个动作:

auto spawn = Spawn::create(
    MoveTo::create(2.0f, Vec2(200, 200)),
    RotateBy::create(2.0f, 360),
    FadeIn::create(2.0f),
    nullptr
);
sprite->runAction(spawn);

Repeat - 重复动作

// Repeat - 重复指定次数
auto repeat = Repeat::create(
    Sequence::create(
        MoveBy::create(0.5f, Vec2(50, 0)),
        MoveBy::create(0.5f, Vec2(-50, 0)),
        nullptr
    ),
    5  // 重复 5 次
);

// RepeatForever - 永久重复
auto repeatForever = RepeatForever::create(
    RotateBy::create(1.0f, 360)
);
sprite->runAction(repeatForever);

DelayTime - 延时动作

auto sequence = Sequence::create(
    MoveTo::create(1.0f, Vec2(100, 0)),
    DelayTime::create(1.0f),  // 延时 1 秒
    MoveTo::create(1.0f, Vec2(200, 0)),
    nullptr
);

ReverseTime - 反向动作

auto move = MoveBy::create(1.0f, Vec2(100, 0));
auto reverse = ReverseTime::create(move->clone());
// 反向执行,相当于 MoveBy(1.0f, Vec2(-100, 0))

缓动动作

缓动动作使用装饰器模式包装其他动作,实现各种缓动效果。

使用缓动

// 创建基础动作
auto move = MoveTo::create(3.0f, Vec2(500, 300));

// 用缓动包装
auto easeMove = EaseElasticOut::create(move);
sprite->runAction(easeMove);

可用缓动类型

缓动类 效果
EaseQuadIn/Out/InOut 二次缓动
EaseCubicIn/Out/InOut 三次缓动
EaseQuartIn/Out/InOut 四次缓动
EaseQuintIn/Out/InOut 五次缓动
EaseSineIn/Out/InOut 正弦缓动
EaseExponentialIn/Out/InOut 指数缓动
EaseCircleIn/Out/InOut 圆形缓动
EaseBackIn/Out/InOut 回震缓动
EaseElasticIn/Out/InOut 弹性缓动
EaseBounceIn/Out/InOut 弹跳缓动

缓动效果说明

  • In: 开始时慢,逐渐加速
  • Out: 开始时快,逐渐减速
  • InOut: 开始和结束时慢,中间快

示例

// 弹性缓动
auto jump = JumpBy::create(2.0f, Vec2(200, 0), 100, 1);
auto elasticJump = EaseElasticOut::create(jump, 0.5f);
sprite->runAction(elasticJump);

// 弹跳缓动
auto scale = ScaleTo::create(1.0f, 2.0f);
auto bounceScale = EaseBounceOut::create(scale);
sprite->runAction(bounceScale);

// 指数缓动
auto move = MoveTo::create(2.0f, Vec2(300, 200));
auto expoMove = EaseExponentialInOut::create(move);
sprite->runAction(expoMove);

自定义缓动

// 使用自定义缓动函数
auto customEase = EaseCustom::create(move, [](float t) {
    // 自定义缓动函数
    return t * t * (3.0f - 2.0f * t);  // smoothstep
});

特殊动作

Speed - 速度控制

动态控制动作的播放速度:

auto move = MoveTo::create(5.0f, Vec2(500, 300));
auto speed = Speed::create(move, 0.5f);  // 半速播放
sprite->runAction(speed);

// 运行时调整速度
speed->setSpeed(2.0f);  // 2 倍速

Follow - 跟随动作

使节点跟随另一个节点移动(常用于相机):

// 无边界跟随
auto follow = Follow::create(player);

// 带边界跟随
Rect boundary(0, 0, 2000, 2000);  // 世界边界
auto followWithBoundary = Follow::create(player, boundary);

camera->runAction(follow);

TargetedAction - 目标动作

在一个节点上运行动作,但作用于另一个节点:

// 在 spriteA 上运行,但影响 spriteB
auto targeted = TargetedAction::create(spriteB, MoveTo::create(2.0f, Vec2(100, 100)));
spriteA->runAction(targeted);

瞬时动作

瞬时动作立即完成,没有动画过程。

CallFunc - 回调动作

// 无参数回调
auto callFunc = CallFunc::create([]() {
    E2D_LOG_INFO("回调执行");
});

// 带节点参数的回调
auto callFuncN = CallFuncN::create([](Node* node) {
    E2D_LOG_INFO("节点: %p", node);
});

Place - 放置动作

// 立即放置到指定位置
auto place = Place::create(Vec2(100, 100));

FlipX/FlipY - 翻转动作

auto flipX = FlipX::create(true);   // 水平翻转
auto flipY = FlipY::create(true);   // 垂直翻转

Show/Hide - 显示/隐藏动作

auto show = Show::create();         // 显示
auto hide = Hide::create();         // 隐藏
auto toggle = ToggleVisibility::create();  // 切换可见性

RemoveSelf - 移除自身

// 从父节点移除
auto removeSelf = RemoveSelf::create();

动作管理

停止动作

// 停止所有动作
sprite->stopAllActions();

// 停止指定动作
sprite->stopAction(action);

// 根据标签停止动作
sprite->stopActionByTag(1);

// 根据标志位停止动作
sprite->stopActionsByFlags(0x01);

查询动作

// 获取动作数量
size_t count = sprite->getActionCount();

// 检查是否有动作在运行
bool running = sprite->isRunningActions();

// 根据标签获取动作
Action* action = sprite->getActionByTag(1);

动作标签和标志位

auto action = MoveTo::create(2.0f, Vec2(100, 100));
action->setTag(1);           // 设置标签
action->setFlags(0x01);      // 设置标志位
sprite->runAction(action);

完整示例

示例 1角色移动动画

void Player::moveTo(const Vec2& target) {
    // 停止当前移动
    stopActionByTag(TAG_MOVE);
    
    // 创建移动动作
    auto move = MoveTo::create(1.0f, target);
    move->setTag(TAG_MOVE);
    
    // 添加缓动效果
    auto easeMove = EaseQuadInOut::create(move);
    
    runAction(easeMove);
}

示例 2UI 弹出动画

void UIPanel::show() {
    // 初始状态
    setScale(0.0f);
    setOpacity(0.0f);
    setVisible(true);
    
    // 并行动画:缩放 + 淡入
    auto spawn = Spawn::create(
        EaseBackOut::create(ScaleTo::create(0.3f, 1.0f)),
        FadeIn::create(0.3f),
        nullptr
    );
    
    runAction(spawn);
}

示例 3游戏结束动画

void GameOverLayer::playAnimation() {
    // 从屏幕底部滑入
    setPosition(Vec2(screenWidth / 2, screenHeight));
    
    auto moveUp = MoveBy::create(1.0f, Vec2(0, -screenHeight));
    moveUp->setCompletionCallback([this]() {
        // 动画完成后启用按钮
        restartBtn_->setEnabled(true);
    });
    
    // 添加缓动效果
    auto easeMove = EaseQuadOut::create(moveUp);
    runAction(easeMove);
}

示例 4复杂序列动画

void Enemy::playDeathAnimation() {
    auto sequence = Sequence::create(
        // 闪烁
        Blink::create(0.5f, 3),
        // 放大
        ScaleTo::create(0.2f, 1.5f),
        // 淡出
        FadeOut::create(0.3f),
        // 移除自身
        RemoveSelf::create(),
        nullptr
    );
    
    runAction(sequence);
}

示例 5循环动画

void Coin::startFloating() {
    // 上下浮动
    auto floatUp = MoveBy::create(0.5f, Vec2(0, 10));
    auto floatDown = floatUp->reverse();
    
    auto floatSequence = Sequence::create(
        EaseSineInOut::create(floatUp),
        EaseSineInOut::create(floatDown),
        nullptr
    );
    
    // 永久循环
    auto floatForever = RepeatForever::create(floatSequence);
    floatForever->setTag(TAG_FLOAT);
    
    runAction(floatForever);
}

性能优化建议

  1. 复用动作:对于频繁使用的动作,可以 clone() 复用
  2. 合理使用标签:便于管理和停止特定动作
  3. 避免过多并发动作:大量节点同时运行动作会影响性能
  4. 及时停止不需要的动作:节点销毁前调用 stopAllActions()

API 参考

Action 基类

方法 说明
isDone() 检查动作是否完成
startWithTarget(node) 启动动作
stop() 停止动作
pause() 暂停动作
resume() 恢复动作
clone() 克隆动作
reverse() 创建反向动作
getTag() / setTag() 获取/设置标签
getFlags() / setFlags() 获取/设置标志位
setCompletionCallback() 设置完成回调

Node 动作接口

方法 说明
runAction(action) 运行动作
stopAllActions() 停止所有动作
stopAction(action) 停止指定动作
stopActionByTag(tag) 根据标签停止动作
stopActionsByFlags(flags) 根据标志位停止动作
getActionByTag(tag) 根据标签获取动作
getActionCount() 获取动作数量
isRunningActions() 检查是否有动作运行