2019-11-23 20:27:39 +08:00
|
|
|
/****************************************************************************
|
|
|
|
Copyright (c) 2008-2010 Ricardo Quesada
|
|
|
|
Copyright (c) 2010-2012 cocos2d-x.org
|
|
|
|
Copyright (c) 2011 Zynga Inc.
|
|
|
|
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
|
|
|
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
|
|
|
|
2022-10-01 16:24:52 +08:00
|
|
|
https://axmolengine.github.io/
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
|
|
in the Software without restriction, including without limitation the rights
|
|
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
|
|
all copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
THE SOFTWARE.
|
|
|
|
****************************************************************************/
|
2023-06-11 13:08:08 +08:00
|
|
|
#include "2d/AnimationCache.h"
|
|
|
|
#include "2d/SpriteFrameCache.h"
|
|
|
|
#include "platform/FileUtils.h"
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
2022-07-11 17:50:21 +08:00
|
|
|
NS_AX_BEGIN
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
AnimationCache* AnimationCache::s_sharedAnimationCache = nullptr;
|
|
|
|
|
|
|
|
AnimationCache* AnimationCache::getInstance()
|
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
if (!s_sharedAnimationCache)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2021-12-08 00:11:53 +08:00
|
|
|
s_sharedAnimationCache = new AnimationCache();
|
2019-11-23 20:27:39 +08:00
|
|
|
s_sharedAnimationCache->init();
|
|
|
|
}
|
|
|
|
|
|
|
|
return s_sharedAnimationCache;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AnimationCache::destroyInstance()
|
|
|
|
{
|
2022-07-16 10:43:05 +08:00
|
|
|
AX_SAFE_RELEASE_NULL(s_sharedAnimationCache);
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool AnimationCache::init()
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
AnimationCache::AnimationCache() {}
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
AnimationCache::~AnimationCache()
|
|
|
|
{
|
2022-07-16 10:43:05 +08:00
|
|
|
AXLOGINFO("deallocing AnimationCache: %p", this);
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
|
|
|
|
2021-12-31 12:12:40 +08:00
|
|
|
void AnimationCache::addAnimation(Animation* animation, std::string_view name)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2021-12-31 12:12:40 +08:00
|
|
|
_animations.insert(std::string{name}, animation);
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
|
|
|
|
2021-12-31 12:12:40 +08:00
|
|
|
void AnimationCache::removeAnimation(std::string_view name)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
if (name.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
_animations.erase(name);
|
|
|
|
}
|
|
|
|
|
2021-12-31 12:12:40 +08:00
|
|
|
Animation* AnimationCache::getAnimation(std::string_view name)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
return _animations.at(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AnimationCache::parseVersion1(const ValueMap& animations)
|
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
SpriteFrameCache* frameCache = SpriteFrameCache::getInstance();
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
for (const auto& anim : animations)
|
|
|
|
{
|
|
|
|
const ValueMap& animationDict = anim.second.asValueMap();
|
|
|
|
const ValueVector& frameNames = animationDict.at("frames").asValueVector();
|
2021-12-25 10:04:45 +08:00
|
|
|
float delay = animationDict.at("delay").asFloat();
|
|
|
|
Animation* animation = nullptr;
|
2019-11-23 20:27:39 +08:00
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
if (frameNames.empty())
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2022-07-16 10:43:05 +08:00
|
|
|
AXLOG(
|
2023-08-31 17:48:39 +08:00
|
|
|
"cocos2d: AnimationCache: Animation '%s' found in dictionary without any frames - cannot add to "
|
2021-12-25 10:04:45 +08:00
|
|
|
"animation cache.",
|
|
|
|
anim.first.c_str());
|
2019-11-23 20:27:39 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t frameNameSize = frameNames.size();
|
|
|
|
Vector<AnimationFrame*> frames(frameNameSize);
|
|
|
|
|
2022-07-21 19:19:08 +08:00
|
|
|
for (auto&& frameName : frameNames)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
SpriteFrame* spriteFrame = frameCache->getSpriteFrameByName(frameName.asString());
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
if (!spriteFrame)
|
|
|
|
{
|
2022-07-16 10:43:05 +08:00
|
|
|
AXLOG(
|
2023-08-31 17:48:39 +08:00
|
|
|
"cocos2d: AnimationCache: Animation '%s' refers to frame '%s' which is not currently in the "
|
2021-12-25 10:04:45 +08:00
|
|
|
"SpriteFrameCache. This frame will not be added to the animation.",
|
|
|
|
anim.first.c_str(), frameName.asString().c_str());
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
AnimationFrame* animFrame = AnimationFrame::create(spriteFrame, 1, ValueMap());
|
|
|
|
frames.pushBack(animFrame);
|
|
|
|
}
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
if (frames.empty())
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2022-07-16 10:43:05 +08:00
|
|
|
AXLOG(
|
2023-08-31 17:48:39 +08:00
|
|
|
"cocos2d: AnimationCache: None of the frames for animation '%s' were found in the SpriteFrameCache. "
|
2021-12-25 10:04:45 +08:00
|
|
|
"Animation is not being added to the Animation Cache.",
|
|
|
|
anim.first.c_str());
|
2019-11-23 20:27:39 +08:00
|
|
|
continue;
|
|
|
|
}
|
2021-12-25 10:04:45 +08:00
|
|
|
else if (frames.size() != frameNameSize)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2022-07-16 10:43:05 +08:00
|
|
|
AXLOG(
|
2023-08-31 17:48:39 +08:00
|
|
|
"cocos2d: AnimationCache: An animation in your dictionary refers to a frame which is not in the "
|
2021-12-25 10:04:45 +08:00
|
|
|
"SpriteFrameCache. Some or all of the frames for the animation '%s' may be missing.",
|
|
|
|
anim.first.c_str());
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
animation = Animation::create(frames, delay, 1);
|
|
|
|
|
|
|
|
AnimationCache::getInstance()->addAnimation(animation, anim.first);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AnimationCache::parseVersion2(const ValueMap& animations)
|
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
SpriteFrameCache* frameCache = SpriteFrameCache::getInstance();
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
for (const auto& anim : animations)
|
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
std::string name = anim.first;
|
2019-11-23 20:27:39 +08:00
|
|
|
ValueMap& animationDict = const_cast<ValueMap&>(anim.second.asValueMap());
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
const Value& loops = animationDict["loops"];
|
2020-08-18 11:33:18 +08:00
|
|
|
bool restoreOriginalFrame = animationDict["restoreOriginalFrame"].asBool();
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
ValueVector& frameArray = animationDict["frames"].asValueVector();
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
if (frameArray.empty())
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2022-07-16 10:43:05 +08:00
|
|
|
AXLOG(
|
2023-08-31 17:48:39 +08:00
|
|
|
"cocos2d: AnimationCache: Animation '%s' found in dictionary without any frames - cannot add to "
|
2021-12-25 10:04:45 +08:00
|
|
|
"animation cache.",
|
|
|
|
name.c_str());
|
2019-11-23 20:27:39 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Array of AnimationFrames
|
|
|
|
Vector<AnimationFrame*> array(static_cast<int>(frameArray.size()));
|
|
|
|
|
2022-07-21 19:19:08 +08:00
|
|
|
for (auto&& obj : frameArray)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
ValueMap& entry = obj.asValueMap();
|
2019-11-23 20:27:39 +08:00
|
|
|
std::string spriteFrameName = entry["spriteframe"].asString();
|
2021-12-25 10:04:45 +08:00
|
|
|
SpriteFrame* spriteFrame = frameCache->getSpriteFrameByName(spriteFrameName);
|
2019-11-23 20:27:39 +08:00
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
if (!spriteFrame)
|
|
|
|
{
|
2022-07-16 10:43:05 +08:00
|
|
|
AXLOG(
|
2023-08-31 17:48:39 +08:00
|
|
|
"cocos2d: AnimationCache: Animation '%s' refers to frame '%s' which is not currently in the "
|
2021-12-25 10:04:45 +08:00
|
|
|
"SpriteFrameCache. This frame will not be added to the animation.",
|
|
|
|
name.c_str(), spriteFrameName.c_str());
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-08-18 11:33:18 +08:00
|
|
|
float delayUnits = entry["delayUnits"].asFloat();
|
2021-12-25 10:04:45 +08:00
|
|
|
Value& userInfo = entry["notification"];
|
2019-11-23 20:27:39 +08:00
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
AnimationFrame* animFrame = AnimationFrame::create(
|
|
|
|
spriteFrame, delayUnits, userInfo.getType() == Value::Type::MAP ? userInfo.asValueMap() : ValueMapNull);
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
array.pushBack(animFrame);
|
|
|
|
}
|
|
|
|
|
2020-08-18 11:33:18 +08:00
|
|
|
float delayPerUnit = animationDict["delayPerUnit"].asFloat();
|
2021-12-25 10:04:45 +08:00
|
|
|
Animation* animation =
|
|
|
|
Animation::create(array, delayPerUnit, loops.getType() != Value::Type::NONE ? loops.asInt() : 1);
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
animation->setRestoreOriginalFrame(restoreOriginalFrame);
|
|
|
|
|
|
|
|
AnimationCache::getInstance()->addAnimation(animation, name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-31 12:12:40 +08:00
|
|
|
void AnimationCache::addAnimationsWithDictionary(const ValueMap& dictionary, std::string_view plist)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
auto anisItr = dictionary.find("animations");
|
2021-12-25 10:04:45 +08:00
|
|
|
if (anisItr == dictionary.end())
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2022-10-01 16:24:52 +08:00
|
|
|
AXLOG("axmol: AnimationCache: No animations were found in provided dictionary.");
|
2019-11-23 20:27:39 +08:00
|
|
|
return;
|
|
|
|
}
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
const Value& animations = anisItr->second;
|
2021-12-25 10:04:45 +08:00
|
|
|
unsigned int version = 1;
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
auto propsItr = dictionary.find("properties");
|
2021-12-25 10:04:45 +08:00
|
|
|
if (propsItr != dictionary.end())
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
const ValueMap& properties = propsItr->second.asValueMap();
|
|
|
|
version = properties.at("format").asInt();
|
2019-11-23 20:27:39 +08:00
|
|
|
const ValueVector& spritesheets = properties.at("spritesheets").asValueVector();
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
for (const auto& value : spritesheets)
|
|
|
|
{
|
|
|
|
std::string path = FileUtils::getInstance()->fullPathFromRelativeFile(value.asString(), plist);
|
2019-11-23 20:27:39 +08:00
|
|
|
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
switch (version)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
parseVersion1(animations.asValueMap());
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
parseVersion2(animations.asValueMap());
|
|
|
|
break;
|
|
|
|
default:
|
2022-07-16 10:43:05 +08:00
|
|
|
AXASSERT(false, "Invalid animation format");
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Read an NSDictionary from a plist file and parse it automatically for animations */
|
2021-12-31 12:12:40 +08:00
|
|
|
void AnimationCache::addAnimationsWithFile(std::string_view plist)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2022-07-16 10:43:05 +08:00
|
|
|
AXASSERT(!plist.empty(), "Invalid texture file name");
|
2021-12-25 10:04:45 +08:00
|
|
|
if (plist.empty())
|
|
|
|
{
|
2019-11-23 20:27:39 +08:00
|
|
|
log("%s error:file name is empty!", __FUNCTION__);
|
|
|
|
return;
|
|
|
|
}
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
ValueMap dict = FileUtils::getInstance()->getValueMapFromFile(plist);
|
|
|
|
|
2022-07-16 10:43:05 +08:00
|
|
|
AXASSERT(!dict.empty(), "CCAnimationCache: File could not be found");
|
2021-12-25 10:04:45 +08:00
|
|
|
if (dict.empty())
|
|
|
|
{
|
2021-12-31 12:12:40 +08:00
|
|
|
log("AnimationCache::addAnimationsWithFile error:%s not exist!", plist.data());
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
addAnimationsWithDictionary(dict, plist);
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
|
|
|
|
2022-07-11 17:50:21 +08:00
|
|
|
NS_AX_END
|