在 cocos2d-x 中如何在动画精灵时加载场景?
How do you load a scene while animating a sprite in cocos2d-x?
我有一个"stage selection"场景和一个"game"场景。然而,当用户按下按钮开始游戏场景时,按下和场景显示之间存在延迟(在旧设备上大约 2 秒或更长时间)。所以我想我应该创建一个加载场景。
所以我现在正在做的是将一个 std::function 传递到我的 "Loading" 场景,它被调用加载场景出现后 0.1 秒。此函数具有启动 "game" 场景的代码,如下所示:
用于创建加载场景。
auto loading_scene = LoadingScene::createLoadingScene([stage_course]() {
Director::getInstance()->replaceScene(Game::createScene(stage_course->course_id));
});
Director::getInstance()->replaceScene(loading_scene);
加载游戏场景。
void LoadingScene::onEnter()
{
Node::onEnter();
call_after(0.1, callback_start);
}
结果是加载场景显示一个简单的角色动画 运行。起初我尝试在回调前延迟 1.0 秒来检查精灵是否正常工作(确实如此,角色运行)。但是当执行回调(加载新场景的回调)时它停止移动并保持这种状态大约 1-2 秒直到场景加载然后呈现..
有谁知道如何在场景加载时保持 sprite 动画,以便在显示 "game" 场景之前它永远不会停止 运行?
编辑:
我用的是cocos2d-x-3.8.
我的加载场景在其初始化函数中有以下代码来创建用于为尖顶设置动画的动画:
// Create the sprite animation
Animation *animation = Animation::create();
for (int i = 0; i < INT16_MAX; i++)
{
string frame_sprite_name = StringUtils::format("Interface/loading/0_%d.png",i);
auto frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(frame_sprite_name);
if (frame) {
animation->addSpriteFrame(frame);
} else {
break;
}
}
animation->setDelayPerUnit(0.15f);
Animate *animate = Animate::create(animation);
// Create a temporal sprite to run the animation
auto temp_sprite = Sprite::create();
temp_sprite->setAnchorPoint(Vec2(0.5,0.5));
temp_sprite->setPosition(Vec2(DISPLAY_WIDTH/2.0f,DISPLAY_HEIGHT/2.0f));
this->addChild(temp_sprite);
temp_sprite->runAction(RepeatForever::create(animate));
编辑 2:
我的游戏场景加载时间太长的原因是因为我正在加载我的舞台需要的所有精灵贴图,如下所示:
// Shared
if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(STUDENTS_SPRITE_MAP)) {
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(STUDENTS_SPRITE_MAP);
}
if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(OTHERS_SPRITE_MAP)) {
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(OTHERS_SPRITE_MAP);
}
if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(INTERFACE_SPRITE_MAP)) {
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(INTERFACE_SPRITE_MAP);
}
if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(ZOMBIES_SPRITE_MAP)) {
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(ZOMBIES_SPRITE_MAP);
}
if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(PORTRAITS_SPRITE_MAP)) {
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(PORTRAITS_SPRITE_MAP);
}
if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(CUTS_SPRITE_MAP)) {
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(CUTS_SPRITE_MAP);
}
// Exclusive
if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(TEACHERS_SPRITE_MAP)) {
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(TEACHERS_SPRITE_MAP);
}
我不知道 cocos2d-x 对此支持得特别好:
CSLoader 在当前线程上加载场景,因此它会阻塞动画循环直到完成。
为了在 CSLoader 正常工作时实现流畅的动画,它需要重构
* 到工作线程中的 运行 - 这会很困难,因为(我上次看的时候)任何 Ref 派生对象如果不是在主 cocos 线程上创建的,都会出现恐慌。
* 频繁调出 Dispatcher / 运行loop 以允许帧动画。我还没有查看 cocostudio::CSLoader 代码来了解它对此的适用性......它肯定不支持开箱即用。
可能难以实现的替代方法是简单地将您的场景分成块 - 每个块都可以快速加载,因此没有明显的加载延迟。
我同意 Chris 所说的,特别是卸载工作或将事情分解成块。
是的,Cocos2d-x 不是为多线程设计的,所以这就是为什么你需要实现一个 update
只做少量工作的回调,然后等到下一个运行循环允许引擎渲染。
我希望我能帮助您解决加载问题,但您显示的代码仅用于加载动画,这不是真正的问题。我们需要查看导致速度变慢的代码以提供一些实用的解决方案。
试试这个:
void HelloWorld::loadTextures1(){
Director::getInstance()->getTextureCache()->addImageAsync("sprites1.png", CC_CALLBACK_1(HelloWorld::loadTextures2, this));
}
void HelloWorld::loadTextures2(Texture2D* texture1){
this->texture1 = texture1;
CCLOG("Texture 1 loaded!");
Director::getInstance()->getTextureCache()->addImageAsync("sprites2.png", CC_CALLBACK_1(HelloWorld::loadTextures3, this));
}
void HelloWorld::loadTextures3(Texture2D* texture2){
this->texture2 = texture2;
CCLOG("Texture 2 loaded!");
Director::getInstance()->getTextureCache()->addImageAsync("sprites3.png", CC_CALLBACK_1(HelloWorld::allTexturesLoaded, this));
}
void HelloWorld::allTexturesLoaded(Texture2D* texture3){
this->texture3 = texture3;
CCLOG("Texture 3 loaded!");
auto cache = SpriteFrameCache::getInstance();
cache->addSpriteFramesWithFile("sprites1.plist", texture1);
cache->addSpriteFramesWithFile("sprites2.plist", texture2);
cache->addSpriteFramesWithFile("sprites3.plist", texture3);
auto scene = GameScene::createScene();
Director::getInstance()->replaceScene(TransitionShrinkGrow::create(.5, scene));
}
它异步加载这 3 个纹理,然后同步加载带有预加载纹理的 plist 文件。最后(同步部分)有点卡顿,但我认为还不错。您也可以深入研究 SpriteFrameCache 并尝试对其进行优化。
我有一个"stage selection"场景和一个"game"场景。然而,当用户按下按钮开始游戏场景时,按下和场景显示之间存在延迟(在旧设备上大约 2 秒或更长时间)。所以我想我应该创建一个加载场景。
所以我现在正在做的是将一个 std::function 传递到我的 "Loading" 场景,它被调用加载场景出现后 0.1 秒。此函数具有启动 "game" 场景的代码,如下所示:
用于创建加载场景。
auto loading_scene = LoadingScene::createLoadingScene([stage_course]() {
Director::getInstance()->replaceScene(Game::createScene(stage_course->course_id));
});
Director::getInstance()->replaceScene(loading_scene);
加载游戏场景。
void LoadingScene::onEnter()
{
Node::onEnter();
call_after(0.1, callback_start);
}
结果是加载场景显示一个简单的角色动画 运行。起初我尝试在回调前延迟 1.0 秒来检查精灵是否正常工作(确实如此,角色运行)。但是当执行回调(加载新场景的回调)时它停止移动并保持这种状态大约 1-2 秒直到场景加载然后呈现..
有谁知道如何在场景加载时保持 sprite 动画,以便在显示 "game" 场景之前它永远不会停止 运行?
编辑:
我用的是cocos2d-x-3.8.
我的加载场景在其初始化函数中有以下代码来创建用于为尖顶设置动画的动画:
// Create the sprite animation
Animation *animation = Animation::create();
for (int i = 0; i < INT16_MAX; i++)
{
string frame_sprite_name = StringUtils::format("Interface/loading/0_%d.png",i);
auto frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(frame_sprite_name);
if (frame) {
animation->addSpriteFrame(frame);
} else {
break;
}
}
animation->setDelayPerUnit(0.15f);
Animate *animate = Animate::create(animation);
// Create a temporal sprite to run the animation
auto temp_sprite = Sprite::create();
temp_sprite->setAnchorPoint(Vec2(0.5,0.5));
temp_sprite->setPosition(Vec2(DISPLAY_WIDTH/2.0f,DISPLAY_HEIGHT/2.0f));
this->addChild(temp_sprite);
temp_sprite->runAction(RepeatForever::create(animate));
编辑 2:
我的游戏场景加载时间太长的原因是因为我正在加载我的舞台需要的所有精灵贴图,如下所示:
// Shared
if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(STUDENTS_SPRITE_MAP)) {
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(STUDENTS_SPRITE_MAP);
}
if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(OTHERS_SPRITE_MAP)) {
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(OTHERS_SPRITE_MAP);
}
if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(INTERFACE_SPRITE_MAP)) {
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(INTERFACE_SPRITE_MAP);
}
if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(ZOMBIES_SPRITE_MAP)) {
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(ZOMBIES_SPRITE_MAP);
}
if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(PORTRAITS_SPRITE_MAP)) {
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(PORTRAITS_SPRITE_MAP);
}
if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(CUTS_SPRITE_MAP)) {
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(CUTS_SPRITE_MAP);
}
// Exclusive
if (!SpriteFrameCache::getInstance()->isSpriteFramesWithFileLoaded(TEACHERS_SPRITE_MAP)) {
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(TEACHERS_SPRITE_MAP);
}
我不知道 cocos2d-x 对此支持得特别好:
CSLoader 在当前线程上加载场景,因此它会阻塞动画循环直到完成。
为了在 CSLoader 正常工作时实现流畅的动画,它需要重构 * 到工作线程中的 运行 - 这会很困难,因为(我上次看的时候)任何 Ref 派生对象如果不是在主 cocos 线程上创建的,都会出现恐慌。 * 频繁调出 Dispatcher / 运行loop 以允许帧动画。我还没有查看 cocostudio::CSLoader 代码来了解它对此的适用性......它肯定不支持开箱即用。
可能难以实现的替代方法是简单地将您的场景分成块 - 每个块都可以快速加载,因此没有明显的加载延迟。
我同意 Chris 所说的,特别是卸载工作或将事情分解成块。
是的,Cocos2d-x 不是为多线程设计的,所以这就是为什么你需要实现一个 update
只做少量工作的回调,然后等到下一个运行循环允许引擎渲染。
我希望我能帮助您解决加载问题,但您显示的代码仅用于加载动画,这不是真正的问题。我们需要查看导致速度变慢的代码以提供一些实用的解决方案。
试试这个:
void HelloWorld::loadTextures1(){
Director::getInstance()->getTextureCache()->addImageAsync("sprites1.png", CC_CALLBACK_1(HelloWorld::loadTextures2, this));
}
void HelloWorld::loadTextures2(Texture2D* texture1){
this->texture1 = texture1;
CCLOG("Texture 1 loaded!");
Director::getInstance()->getTextureCache()->addImageAsync("sprites2.png", CC_CALLBACK_1(HelloWorld::loadTextures3, this));
}
void HelloWorld::loadTextures3(Texture2D* texture2){
this->texture2 = texture2;
CCLOG("Texture 2 loaded!");
Director::getInstance()->getTextureCache()->addImageAsync("sprites3.png", CC_CALLBACK_1(HelloWorld::allTexturesLoaded, this));
}
void HelloWorld::allTexturesLoaded(Texture2D* texture3){
this->texture3 = texture3;
CCLOG("Texture 3 loaded!");
auto cache = SpriteFrameCache::getInstance();
cache->addSpriteFramesWithFile("sprites1.plist", texture1);
cache->addSpriteFramesWithFile("sprites2.plist", texture2);
cache->addSpriteFramesWithFile("sprites3.plist", texture3);
auto scene = GameScene::createScene();
Director::getInstance()->replaceScene(TransitionShrinkGrow::create(.5, scene));
}
它异步加载这 3 个纹理,然后同步加载带有预加载纹理的 plist 文件。最后(同步部分)有点卡顿,但我认为还不错。您也可以深入研究 SpriteFrameCache 并尝试对其进行优化。