| 后缀 | 格式 | 说明 |
|---|---|---|
.red |
XML (plist) | Redream 编辑器的场景/UI 源文件,包含节点树结构和时间线定义 |
.rebolt |
JSON | Redream 编辑器的行为树配置源文件,定义交互逻辑流程 |
.anim |
XML (plist) | Redream 编辑器的动画曲线源文件,定义单条可复用的路径动画 |
| 后缀 | 格式 | 说明 |
|---|---|---|
.redream |
Protobuf 二进制 | 由 .red(+ 可选的 .rebolt)编译合并而来,是代码中实际加载的文件 |
.redanim |
Protobuf 二进制 | 由 .anim 编译而来,是动画曲线的运行时文件 |
.xml |
XML | 行为树的运行时文件(Behaviac 格式),由 .rebolt 中的行为树定义编译而来 |
.batchredream |
Protobuf 二进制 | 纹理合批优化版本的 .redream,加载嵌套子文件时自动回退查找 |
编辑器源文件 (ccb/) → 编译 → 运行时文件 (_ccbi/)
───────────────────────────────────────────────────────────────
xxx.red → xxx.redream
xxx.red + xxx.rebolt → xxx.redream + 若干 .xml
xxx.anim → xxx.redanim
构建打包时只有 _ccbi/ 目录下的文件会进入 app。代码中加载时写 .redream 后缀(或不写后缀,REDReader 会自动补 .redream)。
.red 的文件:编译后只生成 .redream,只包含节点树和时间线,没有交互逻辑.red + .rebolt 的文件:编译后生成 .redream(节点树+时间线+行为树引用)+ .xml(行为树定义),支持行为树驱动的交互逻辑auto* layer = redutils::QCoreLayer::Layer("xxx.redream");
parentNode->addChild(layer);
加载 .redream 文件,自动把内部子节点按变量名存入字典,自动创建 QCoreLayerDelegate 挂到 REDAnimationManager 上接收动画完成回调。内置 setTimelineCallback 机制,可以直接用 lambda 注册时间线关键帧回调,不需要自己实现任何接口。
auto* loader = RedreamLoader::Layer("xxx.redream");
parentNode->addChild(loader);
加载 .redream 文件,自动存子节点到字典。不会自动创建 delegate,onResolveREDCCCallFuncSelector 默认返回 nullptr(不处理时间线回调)。比 QCoreLayer 更轻量,适合只需要静态渲染 + 简单 playAnim 的场景。
auto* loader = RedreamLoader::ReboltLayer("xxx.redream");
parentNode->addChild(loader);
在 Layer 的基础上,额外从 REDReader 中取出 ReboltRedManager,调用 retain() 持有它,并把自己设为 NotifyDevelopmentDelegate。这样 .redream 文件中配置的行为树才能运行,行为树中的"通知工程师"节点才能触发回调。如果 .redream 文件里没有配置行为树,和 Layer 没有区别。
auto* layer = redutils::RUReboltLayer::createReboltLayer("xxx.redream");
parentNode->addChild(layer);
layer->runBehaviacWhitFunName("初始化");
更高层的 Rebolt 封装,内部也是通过 REDReader 加载,自动设置 NotifyDevelopmentDelegate,并提供 registerOnNotify 等便捷接口。
auto* lib = redream::NodeLoaderLibrary::newDefaultNodeLoaderLibrary();
lib->registerNodeLoader("RedreamLoader", RedreamLoaderLoader::loader());
auto* loader = RedreamLoader::LayerWithLib("xxx.redream", lib);
当 .redream 文件中使用了自定义节点类型时,需要注册对应的 NodeLoader。
// 写 .redream 后缀(推荐)
auto* layer = RedreamLoader::Layer("xxx.redream");
// 不写后缀,REDReader 自动补 .redream
auto* layer = RedreamLoader::Layer("xxx");
// 两者等价
REDReader::readNodeGraphFromFile 的逻辑:如果传入的文件名没有后缀(找不到 .),自动追加 .redream;如果有后缀则原样使用。
时间线是 .redream 文件的核心概念。一个 .redream 文件可以包含多条命名时间线,每条时间线(REDSequence)包含:
name — 名称(如 "常态"、"动画_解锁")duration — 时长(秒)sequenceId — 唯一 IDchainedSequenceId — 链式播放的下一条时间线 ID(-1 表示没有后续)callbackChannel — 回调关键帧通道(时间线上的函数调用点)soundChannel — 音效通道wiseChannel — Wwise 音频通道shakeChannel / shake2Channel — 震动通道在 Redream 编辑器中可以设置一条时间线播完后自动接着播另一条。代码中体现在 sequenceCompleted 里:
int nextSeqId = sequence->getChainedSequenceId();
if (nextSeqId != -1) {
// 当前时间线完成,自动播放下一条
delegate->completedAnimationAndPlayNextSequenceNamed(...);
delegate->completedAnimationSequenceNamed(...);
runAnimationsForSequenceIdTweenDuration(nextSeqId, 0, callback);
} else {
// 真正播完了,没有后续
delegate->completedAnimationSequenceNamed(...);
}
// RedreamLoader / QCoreLayer 的简化接口
float duration = loader->playAnim("常态"); // 播放并返回时长
float time = loader->getAnimTime("动画_解锁"); // 仅获取时长
// QCoreLayer 支持播完回调
coreLayer->playAnim("动画名", [](float dt) {
// 动画播完后执行
});
// 播放后自动从父节点移除自己
coreLayer->playAnimAndRemoveSelf("out");
// 内部实现:播放时间线,注册完成回调,回调中调用 this->removeFromParent()
// 检查时间线是否存在
bool valid = coreLayer->isValidAnim("某条时间线");
// 从节点获取动画管理器
auto* animMgr = REDAnimationManager::fromNode(node);
// 或者
auto* animMgr = dynamic_cast<REDAnimationManager*>(node->getUserObject());
// 按名称播放
animMgr->runAnimationsForSequenceNamed("timeline_name");
// 带过渡时间和完成回调
animMgr->runAnimationsForSequenceNamedTweenDuration("name", 0.3f,
[](Node* n, AnimationCompleteType type) {
// type 取值:
// CHAINED — 播完但会继续播下一条链式时间线
// COMPLETED — 完全播放完毕
// STOPED — 被 stopAllNodeAction() 停止
});
// 按 ID 播放
int seqId = animMgr->getSequenceId("timeline_name");
animMgr->runAnimationsForSequenceIdTweenDuration(seqId, 0);
// 获取时间线时长
float dur = animMgr->getSequenceDuration("timeline_name");
// 刷新到首帧状态(不播放动画,只设置属性到第一帧的值)
animMgr->refreshStatusOnFirstFream(seqId, 0);
// 动画速度控制
animMgr->setAnimationSpeed(2.0f); // 2 倍速
coreLayer->setAnimationSpeed(0.5f); // 半速
// RedreamLoader 方式 — 停止该节点上的所有 cocos2d Action
loader->stopAnimAll(); // 内部调用 this->stopAllActions()
// REDAnimationManager 方式 — 停止所有节点动画并触发回调
animMgr->stopAllNodeAction();
stopAllNodeAction() 会:
1. 停止所有节点上的 Action
2. 触发 delegate 的 stopAnimationSequenceNamed 回调
回调触发的优先级:
- 优先级 1:尝试把 _rootNode 强转为 REDAnimationManagerDelegate,如果成功则调用它
- 优先级 2:使用通过 setDelegate() 设置的 _delegate(QCoreLayer 加载时自动设置的 QCoreLayerDelegate)
- 优先级 3:使用通过 runAnimationsWithListen() 传入的一次性 _listen(调用后自动置空)
没有专门的"重新开始"接口。直接再次调用 playAnim 即可,它会停止当前正在播放的时间线并开始新的:
loader->playAnim("常态"); // 重新播放
加载 .redream 文件后,节点树已经构建完毕,可以直接作为静态 UI 使用,不需要播放任何时间线:
auto* loader = RedreamLoader::Layer("SL_棋盘模块_基本元素_棋盘格.redream");
parentNode->addChild(loader);
// 获取内部子节点进行静态操作
Sprite* bg = loader->getSprite("棋盘格");
bg->setOpacity(128);
Rebolt 是 Redream 的行为树驱动系统,用于动态交互逻辑。行为树在 Redream 编辑器中配置,运行时由 ReboltRedManager 驱动。
// 加载带 Rebolt 的文件
auto* loader = RedreamLoader::ReboltLayer("xxx.redream");
parentNode->addChild(loader);
// 获取 Rebolt 管理器
auto* reboltMgr = loader->getReboltManager();
Rebolt 行为树内部可以执行的操作(这些是行为树节点自动调用的,不需要程序员手动调用):
playTimeLine("actionId", errorInfo) — 播放时间线stopTimeLine(errorInfo) — 停止时间线showNode("nodeName", errorInfo) — 显示节点(内部调用 node->setVisible(true))setLabelTitle("label", "text", errorInfo) — 设置文本setCustomDataVar("key", "value") — 设置自定义变量setCoderDataVar("key", "value") — 设置程序变量setGlobalDataVar("key", "value") — 设置全局变量setSpriteImage("nodeId", "imagePath", errorInfo) — 设置精灵图片runBehaviacTree("treeName", boolMap, stringMap) — 运行行为树runBehaviacWhitFunName("funcName") — 按函数名运行行为树行为树中有一种节点叫"通知工程师"(Notify Development),用于行为树和 C++ 代码之间的通信:
NotifyDevelopmentWaiter,调用 delegate->onNotifyDevelopment(mgr, waiter, notify, param, isWait, outNode)notify(通知名称)和 param(参数)执行自定义逻辑isWait == true,行为树暂停等待,程序员完成后调用 waiter->notifyDevelopmentEnd() 让行为树继续isWait == false,行为树不等待,直接继续执行RedreamLoader 已经实现了 NotifyDevelopmentDelegate 接口并暴露了 lambda:
auto* loader = RedreamLoader::ReboltLayer("xxx.redream");
loader->_reboltCallBack = [](ReboltRedManager* mgr, NotifyDevelopmentWaiter* waiter,
std::string notify, std::string param, bool isWait, Node* outNode) {
if (notify == "doSomething") {
// 执行自定义逻辑
}
if (isWait) {
waiter->notifyDevelopmentEnd(); // 通知行为树继续
}
};
// 直接操作 cocos2d Node 的可见性
loader->setVisible(true);
loader->setVisible(false);
// 操作内部子节点
Node* child = loader->getNode("某个节点");
child->setVisible(false);
// 透明度控制
Sprite* sprite = loader->getSprite("棋盘格");
sprite->setOpacity(0); // 完全透明
sprite->setOpacity(255); // 完全不透明
setVisible(false) 只是不渲染,节点仍然在节点树中,不会被释放。
// 通过变量名获取 redream 文件中的定位点节点
Node* locator = loader->getNode("元素定位点");
// 局部坐标 → 世界坐标
Vec2 worldPos = locator->convertToWorldSpace(Vec2::ZERO);
// 世界坐标 → 另一个节点的局部坐标
Vec2 localPos = targetParent->convertToNodeSpace(worldPos);
行为树内部可以获取节点的全局坐标(这些是行为树节点自动调用的):
getNodeGlobalPostionX("nodeName", errorInfo) — 获取节点全局 X 坐标getNodeGlobalPostionY("nodeName", errorInfo) — 获取节点全局 Y 坐标getSubredNodeGlobalPostionX("redPath", "nodeName", errorInfo) — 获取子 red 文件中节点的全局 XgetSubredNodeGlobalPostionY("redPath", "nodeName", errorInfo) — 获取子 red 文件中节点的全局 Y// redream 库提供的绝对位置计算工具
Vec2 absPos = redream::getAbsolutePosition(pt, posType, containerSize, propName);
RedreamLoader 和 QCoreLayer 都提供按变量名获取子节点的方法。变量名在 Redream 编辑器中设置。
| 方法 | 返回类型 | 说明 |
|---|---|---|
getNode("name") |
Node* |
最基础的节点,只有位置、缩放、旋转、可见性等几何属性。用于定位点、容器等 |
getSprite("name") |
Sprite* |
精灵,显示一张图片。有 setTexture、setSpriteFrame、setOpacity 等 |
getLabel("name") |
Label* |
cocos2d 原生文本节点,支持 TTF/BMFont,有 setString、setColor 等 |
getRedLabel("name") |
RedLabel* |
Redream 自定义的双层文本节点(见下方说明) |
getParticle("name") |
ParticleSystemQuad* |
粒子系统节点 |
getREDNodeButton("name") |
REDNodeButton* |
Redream 自定义按钮(见下方说明) |
getSubLoader("name") |
RedreamLoader* |
嵌套的子 red 文件节点 |
QCoreLayer 额外提供:
| 方法 | 返回类型 | 说明 |
|---|---|---|
getCoreLayer("name") |
QCoreLayer* |
QCoreLayer 版本的嵌套子 red 文件 |
getCoreBtn("name") |
QCoreBtn* |
QCoreLayer 体系下的按钮封装 |
getCustomeProperty("name") |
Value |
获取 Redream 编辑器中设置的自定义属性值 |
RedLabel 继承自 Node,内部包含两个 Label:一个前景、一个背景,用于实现描边/阴影效果。
RedLabel* label = loader->getRedLabel("标题");
label->setString("Hello"); // 同时设置前景和背景文本
label->setFrontColor(Color3B::WHITE); // 前景色
label->setBackColor(Color3B::BLACK); // 背景色(描边/阴影)
label->setFrontOpacity(255); // 前景透明度
label->setBackOpacity(128); // 背景透明度
label->setFrontBMFontFilePath("font.fnt"); // 前景字体
label->setBackBMFontFilePath("font_bg.fnt"); // 背景字体
label->setDimensions(200, 50); // 文本框尺寸
label->setHorizontalAlignment(TextHAlignment::CENTER); // 水平对齐
label->setVerticalAlignment(TextVAlignment::CENTER); // 垂直对齐
label->enableWrap(true); // 自动换行
label->setLineSpacing(5.0f); // 行间距
REDNodeButton 继承自 cocos2d::extension::Control,支持触摸事件和按下缩放效果。
REDNodeButton* btn = loader->getREDNodeButton("按钮");
btn->setZoomOnTouchDown(true); // 按下时缩放
btn->setScaleRatio(1.1f); // 按下缩放比例
btn->setSwallowsTouches(true); // 吞噬触摸事件
btn->setEnabled(true); // 启用/禁用
// 触摸移动取消(如在 TableView 中的按钮,滑动时取消点击)
btn->setTouchMovedCanceled(true, 10.0f);
项目中有两套不同的"自定义函数"机制。
在 Redream 编辑器中,可以在时间线的某个时间点插入一个"回调"关键帧,指定一个函数名。播放到那个时间点时,引擎会查找并调用对应的 C++ 函数。
QCoreLayer 已经封装好了这个机制,提供直接可用的 API:
auto* layer = redutils::QCoreLayer::Layer("xxx.redream");
// 直接用 lambda 注册时间线关键帧回调
layer->setTimelineCallback("myCallback", []() {
// 时间线播到这个关键帧时执行
});
layer->playAnim("某条时间线");
内部原理:QCoreLayer 在 onResolveREDCCCallFuncSelector 中注册了 EventListenerCustom,监听 "__CCCallFuncSelector__" + selectorName 事件。当时间线播到回调帧时 dispatch 该事件,然后从 _timelineCallbacks map 中找到对应的 lambda 执行。
监听时间线播放完成事件:
// QCoreLayer 方式 — 直接用 lambda
coreLayer->registerCompletedAniCallBack([](string timeLineName) {
// timeLineName 是播完的时间线名称
if (timeLineName == "动画_解锁") {
// 处理完成逻辑
}
});
// QCoreLayer 的 playAnim 也支持完成回调
coreLayer->playAnim("动画名", [](float dt) {
// 播完后执行(通过 scheduleOnce 实现)
});
行为树系统的回调机制,见第五章"Rebolt 的通知工程师机制"。
与机制 A 的区别: - 机制 A 是"时间线播到某个时间点 → 触发回调",用于动画过程中的事件 - 机制 C 是"行为树执行到某个逻辑节点 → 通知程序",用于交互逻辑流程中需要程序介入的地方
成员变量绑定是 Redream 编辑器和 C++ 代码之间的节点引用机制。在 Redream 编辑器中,可以给任意节点设置一个"变量名"。加载 .redream 文件时,引擎会对每个有变量名的节点调用 onAssignREDMemberVariable(target, name, node),把节点指针传给 C++ 代码。
RedreamLoader 和 QCoreLayer 都已经实现了这个接口,把所有带变量名的节点存入 _mapChilden 字典:
// RedreamLoader 的实现
bool RedreamLoader::onAssignREDMemberVariable(Ref* pTarget, const char* name, Node* node) {
if (pTarget == this && node != this && strcmp(name, "") != 0) {
_mapChilden.insert(pair<string, Node*>(string(name), node));
return true;
}
return false;
}
这就是为什么可以通过 loader->getNode("元素定位点") 按名称获取节点——这些名称就是编辑器中设置的变量名,在加载时通过成员变量绑定机制存入了字典。
好处:让程序可以按名称引用编辑器中的任意节点,而不需要通过遍历子节点树或硬编码层级关系来查找。编辑器中调整了节点层级结构,只要变量名不变,代码就不需要改。
.anim(编辑器源文件)/ .redanim(编译后运行时文件)是从 .red 文件中提取出来的单条动画曲线,用于在代码中动态应用到任意节点上(比如飞行路径动画)。
与 .redream 的区别:.redream 包含完整的节点树 + 多条时间线;.redanim 只包含一个节点、一条时间线的动画曲线数据,不包含节点树。
// 从 .redanim 文件创建(传入文件名,内部自动补 .redanim 后缀)
auto* anim = redream::RedreamAnim::createFromAnimFile("path/to/anim");
// 获取动画时长
float duration = anim->getDuration();
// 设置起止位置(覆盖文件中的默认值,用于路径动画)
anim->setStartPosition(Vec2(100, 200));
anim->setEndPosition(Vec2(500, 300));
// 生成 cocos2d Action(autorelease)
// rotateWith: 是否沿路径方向旋转
Action* action = anim->getAction(true);
// 应用到任意节点
myNode->runAction(action);
行为树中可以通过"播放动画文件"节点使用 .redanim:
// ReboltRedManager 内部实现
void ReboltRedManager::runAnimFile(Node* node, std::string animPath,
Vec2 startPos, Vec2 endPos, bool rotateWith,
const std::function<void()>& func, ReboltErrorInfo& errorInfo) {
Node* par = node->getParent();
Vec2 localStartPos = par->convertToNodeSpace(startPos);
Vec2 localEndPos = par->convertToNodeSpace(endPos);
auto anim = redream::RedreamAnim::createFromAnimFile(animPath);
anim->setStartPosition(localStartPos);
anim->setEndPosition(localEndPos);
auto seq = Sequence::create(anim->getAction(rotateWith), CallFunc::create(func), NULL);
node->runAction(seq);
}
.redanim 文件支持以下属性的关键帧动画:
- position — 位置(支持贝塞尔曲线路径)
- scale — 缩放
- rotation — 旋转
- opacity — 透明度
- skew — 倾斜
- displayFrame — 纹理帧动画(在 .redanim 中会被忽略,无意义)
关键帧支持丰富的缓动曲线:
| 类别 | 类型 |
|---|---|
| 基础 | LINEAR、INSTANT |
| 三次 | CUBIC_IN、CUBIC_OUT、CUBIC_INOUT |
| 弹性 | ELASTIC_IN、ELASTIC_OUT、ELASTIC_INOUT |
| 弹跳 | BOUNCE_IN、BOUNCE_OUT、BOUNCE_INOUT |
| 回弹 | BACK_IN、BACK_OUT、BACK_INOUT |
| 正弦 | SineIn、SineOut、SineInOut |
| 指数 | ExponentialIn、ExponentialOut、ExponentialInOut |
| 圆形 | CircleActionIn、CircleActionOut、CircleActionInOut |
| 2 次方 | QuadraticActionIn、QuadraticActionOut、QuadraticActionInOut |
| 3 次方 | CubicActionIn、CubicActionOut、CubicActionInOut |
| 4 次方 | QuarticActionIn、QuarticActionOut、QuarticActionInOut |
| 5 次方 | QuinticActionIn、QuinticActionOut、QuinticActionInOut |
| 自定义 | Custom |
cocos2d 的场景是一棵节点树。每个 Node 可以有一个 parent 和多个 children。
parent->addChild(child); // child 加入 parent 的子节点列表,引用计数 +1
parent->addChild(child, zOrder); // 指定渲染层级
child->removeFromParent(); // 从 parent 中移除,引用计数 -1
parent->removeChild(child); // 同上
parent->removeAllChildren(); // 移除所有子节点
parent->getChildren(); // 获取所有子节点
child->getParent(); // 获取父节点
子节点的坐标、缩放、旋转、可见性都相对于父节点。父节点被移除时,所有子节点也会被移除。
addChild 只需要调用一次。调用后 child 就一直存在于 parent 的子节点列表中,每帧都会被渲染,直到主动调用 removeFromParent() 或 removeChild() 把它移除。不需要每帧 addChild。
一个节点只能有一个 parent。如果对已经有 parent 的节点再次 addChild,会出错。
cocos2d 使用引用计数管理内存。所有继承自 Ref 的对象都有引用计数。
obj->retain(); // 引用计数 +1(你想持有它时调用)
obj->release(); // 引用计数 -1(不再需要时调用)
obj->autorelease(); // 放入自动释放池,这一帧结束时自动 release 一次
// 引用计数归 0 时自动 delete
cocos2d 中几乎所有类都遵循 CREATE_FUNC 宏定义的创建模式:
// CREATE_FUNC 宏展开后等价于:
static MyType* create() {
MyType* pRet = new(std::nothrow) MyType(); // 堆上分配,引用计数 = 1
if (pRet && pRet->init()) {
pRet->autorelease(); // 放入自动释放池
return pRet;
} else {
delete pRet;
return nullptr;
}
}
auto* sprite = Sprite::create("image.png");
// 此时引用计数 = 1,且已标记 autorelease
// 如果这一帧内没有人 retain 它(比如 addChild),帧末它就会被释放
parent->addChild(sprite);
// addChild 内部调用 sprite->retain(),引用计数 = 2
// 帧末 autorelease 池清理,release 一次,引用计数 = 1
// sprite 安全存活
parent->removeChild(sprite);
// removeChild 内部调用 sprite->release(),引用计数 = 0
// 触发 delete,sprite 被析构
如果你要在成员变量中持有一个不在节点树中的 Ref 对象,需要手动 retain/release:
// RedreamLoader::ReboltLayer 中的实际例子
ReboltRedManager* reboltM = reboltReader->getReboltRedManager();
reboltM->retain(); // 手动 retain,因为 ReboltRedManager 不会被 addChild
// 对应地,在析构函数中必须手动 release:
RedreamLoader::~RedreamLoader() {
if (_loaderReboltRedManager != nullptr) {
_loaderReboltRedManager->release();
_loaderReboltRedManager = nullptr;
}
}
addChild 会自动 retain,removeChild / removeFromParent 会自动 release → 不需要手动管理Ref 对象 → 手动 retain,析构时手动 releasecreate() 返回的对象已经 autorelease,如果这一帧内既没有 addChild 也没有 retain,帧末就会被自动释放(变成野指针)// 每帧调用
node->schedule([](float dt) { /* dt 是距上一帧的秒数 */ }, "key");
// 延迟一次性调用
node->scheduleOnce([](float dt) { /* 只执行一次 */ }, 2.0f, "key");
// 固定间隔调用
node->schedule([](float dt) { }, 0.5f, "key"); // 每 0.5 秒
// 取消定时器
node->unschedule("key");
QCoreLayer::playAnim 中就用了 scheduleOnce 来实现"动画播完后回调":
void QCoreLayer::playAnim(std::string animName, const std::function<void(float)>& cbFinishAnim, std::string cbScheduleKey) {
_mAnimCtrl->runAnimationsForSequenceNamed(animName.c_str());
if (cbFinishAnim) {
float animTime = getAnimTime(animName);
scheduleOnce(cbFinishAnim, animTime, cbScheduleKey);
}
}
项目中定义了 IRedream 基类,约定子类在 initRebolt 中完成资源加载和初始化:
class IRedream : public cocos2d::Node {
public:
virtual void initRebolt() = 0;
};
class KeyRedream : public IRedream {
void initRebolt() override {
_redream = RedreamLoader::Layer(_file); // 加载
addChild(_redream); // 挂到场景树
_redream->playAnim("常态"); // 播放默认时间线
}
void refreshVisualState(int type) {
_redream->playAnim("常态"); // 切换状态时重新播放
}
private:
std::string _file = "YJ_棋盘模块_特殊元素_钥匙.redream";
RedreamLoader* _redream = nullptr;
};
class SpoolCellRedream : public IRedream {
void initRebolt() override {
_redream = RedreamLoader::Layer(_file);
addChild(_redream);
}
void setCellSize(float width, float height) {
Sprite* bgSprite = _redream->getSprite("棋盘格");
Size originalSize = bgSprite->getContentSize();
float scale = std::min(width / originalSize.width, height / originalSize.height);
_contentRoot->setScale(scale);
}
Node* getElementLocatorNode() const {
return _redream->getNode("元素定位点");
}
Node* getBundleLocatorNode(BundleLocatorType type) const {
switch (type) {
case BundleLocatorType::HorizontalBackLeftTop:
return _redream->getNode("绳子横向_左上");
// ...
}
}
private:
std::string _file = "SL_棋盘模块_基本元素_棋盘格.redream";
RedreamLoader* _redream = nullptr;
};
// 播放动画并在完成后恢复常态
_redream->playAnim("动画_激活");
const float delay = _redream->getAnimTime("动画_激活");
runAction(Sequence::create(
DelayTime::create(delay),
CallFunc::create([this]() {
_redream->playAnim("常态");
}),
nullptr
));
auto* efx = redutils::QCoreLayer::Layer("shop_loading.redream");
scene->addChild(efx);
efx->playAnim("1");
// 播完 "out" 动画后自动从场景中移除并释放
efx->playAnimAndRemoveSelf("out");
auto* layer = redutils::RUReboltLayer::createReboltLayer("shop_interface.redream");
parentNode->addChild(layer);
// 注册通知回调
layer->registerOnNotify([this](const redutils::ReboltNotifyData& data) {
// 处理行为树发出的通知
});
// 运行行为树中的指定函数
layer->runBehaviacWhitFunName("初始化");