Extra2D/docs/API_Tutorial/09_Action_System.md

599 lines
13 KiB
Markdown
Raw Normal View 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()` 创建:
```cpp
// 创建移动动作
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()` 方法运行动作:
```cpp
sprite->runAction(move);
```
### 动作完成回调
使用 `setCompletionCallback()` 设置动作完成时的回调:
```cpp
auto action = MoveTo::create(2.0f, Vec2(100, 100));
action->setCompletionCallback([]() {
E2D_LOG_INFO("动作完成!");
});
sprite->runAction(action);
```
---
## 基本动作
### 移动动作
```cpp
// MoveTo - 移动到指定位置
auto moveTo = MoveTo::create(2.0f, Vec2(100, 100));
// MoveBy - 相对移动
auto moveBy = MoveBy::create(2.0f, Vec2(50, 0)); // 向右移动 50 像素
```
### 跳跃动作
```cpp
// 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 像素
```
### 贝塞尔曲线动作
```cpp
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);
```
### 缩放动作
```cpp
// 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); // 缩小一半
```
### 旋转动作
```cpp
// RotateTo - 旋转到指定角度
auto rotateTo = RotateTo::create(2.0f, 90.0f); // 旋转到 90 度
// RotateBy - 相对旋转
auto rotateBy = RotateBy::create(2.0f, 360.0f); // 旋转 360 度
```
### 淡入淡出动作
```cpp
// 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% 透明度
```
### 闪烁动作
```cpp
// Blink - 闪烁
auto blink = Blink::create(2.0f, 5); // 2 秒内闪烁 5 次
```
### 色调动作
```cpp
// TintTo - 变化到指定颜色
auto tintTo = TintTo::create(1.0f, 255, 0, 0); // 变为红色
// TintBy - 相对颜色变化
auto tintBy = TintBy::create(1.0f, 50, 0, 0); // 红色通道增加 50
```
---
## 组合动作
### Sequence - 序列动作
按顺序依次执行多个动作:
```cpp
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 - 并行动作
同时执行多个动作:
```cpp
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 - 重复动作
```cpp
// 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 - 延时动作
```cpp
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 - 反向动作
```cpp
auto move = MoveBy::create(1.0f, Vec2(100, 0));
auto reverse = ReverseTime::create(move->clone());
// 反向执行,相当于 MoveBy(1.0f, Vec2(-100, 0))
```
---
## 缓动动作
缓动动作使用装饰器模式包装其他动作,实现各种缓动效果。
### 使用缓动
```cpp
// 创建基础动作
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**: 开始和结束时慢,中间快
### 示例
```cpp
// 弹性缓动
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);
```
### 自定义缓动
```cpp
// 使用自定义缓动函数
auto customEase = EaseCustom::create(move, [](float t) {
// 自定义缓动函数
return t * t * (3.0f - 2.0f * t); // smoothstep
});
```
---
## 特殊动作
### Speed - 速度控制
动态控制动作的播放速度:
```cpp
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 - 跟随动作
使节点跟随另一个节点移动(常用于相机):
```cpp
// 无边界跟随
auto follow = Follow::create(player);
// 带边界跟随
Rect boundary(0, 0, 2000, 2000); // 世界边界
auto followWithBoundary = Follow::create(player, boundary);
camera->runAction(follow);
```
### TargetedAction - 目标动作
在一个节点上运行动作,但作用于另一个节点:
```cpp
// 在 spriteA 上运行,但影响 spriteB
auto targeted = TargetedAction::create(spriteB, MoveTo::create(2.0f, Vec2(100, 100)));
spriteA->runAction(targeted);
```
---
## 瞬时动作
瞬时动作立即完成,没有动画过程。
### CallFunc - 回调动作
```cpp
// 无参数回调
auto callFunc = CallFunc::create([]() {
E2D_LOG_INFO("回调执行");
});
// 带节点参数的回调
auto callFuncN = CallFuncN::create([](Node* node) {
E2D_LOG_INFO("节点: %p", node);
});
```
### Place - 放置动作
```cpp
// 立即放置到指定位置
auto place = Place::create(Vec2(100, 100));
```
### FlipX/FlipY - 翻转动作
```cpp
auto flipX = FlipX::create(true); // 水平翻转
auto flipY = FlipY::create(true); // 垂直翻转
```
### Show/Hide - 显示/隐藏动作
```cpp
auto show = Show::create(); // 显示
auto hide = Hide::create(); // 隐藏
auto toggle = ToggleVisibility::create(); // 切换可见性
```
### RemoveSelf - 移除自身
```cpp
// 从父节点移除
auto removeSelf = RemoveSelf::create();
```
---
## 动作管理
### 停止动作
```cpp
// 停止所有动作
sprite->stopAllActions();
// 停止指定动作
sprite->stopAction(action);
// 根据标签停止动作
sprite->stopActionByTag(1);
// 根据标志位停止动作
sprite->stopActionsByFlags(0x01);
```
### 查询动作
```cpp
// 获取动作数量
size_t count = sprite->getActionCount();
// 检查是否有动作在运行
bool running = sprite->isRunningActions();
// 根据标签获取动作
Action* action = sprite->getActionByTag(1);
```
### 动作标签和标志位
```cpp
auto action = MoveTo::create(2.0f, Vec2(100, 100));
action->setTag(1); // 设置标签
action->setFlags(0x01); // 设置标志位
sprite->runAction(action);
```
---
## 完整示例
### 示例 1角色移动动画
```cpp
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 弹出动画
```cpp
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游戏结束动画
```cpp
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复杂序列动画
```cpp
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循环动画
```cpp
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()` | 检查是否有动作运行 |