2019-11-23 20:27:39 +08:00
|
|
|
/****************************************************************************
|
|
|
|
Copyright (c) 2013 cocos2d-x.org
|
2019-11-25 01:35:26 +08:00
|
|
|
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
|
|
|
Copyright (c) 2019-2020 simdsoft, @HALX99
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
http://www.cocos2d-x.org
|
|
|
|
|
|
|
|
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.
|
|
|
|
****************************************************************************/
|
|
|
|
|
2020-08-03 20:31:47 +08:00
|
|
|
#include "cocostudio/ActionTimeline/CCActionTimeline.h"
|
2019-11-23 20:27:39 +08:00
|
|
|
|
2020-08-03 20:31:47 +08:00
|
|
|
#include "cocostudio/CCComExtensionData.h"
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
USING_NS_CC;
|
|
|
|
|
|
|
|
NS_TIMELINE_BEGIN
|
|
|
|
|
|
|
|
// ActionTimeline
|
|
|
|
ActionTimeline* ActionTimeline::create()
|
|
|
|
{
|
|
|
|
ActionTimeline* object = new (std::nothrow) ActionTimeline();
|
|
|
|
if (object && object->init())
|
|
|
|
{
|
|
|
|
object->autorelease();
|
|
|
|
return object;
|
|
|
|
}
|
|
|
|
CC_SAFE_DELETE(object);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
ActionTimeline::ActionTimeline()
|
|
|
|
: _duration(0)
|
|
|
|
, _time(0)
|
|
|
|
, _timeSpeed(1)
|
|
|
|
, _frameInternal(1/60.0f)
|
|
|
|
, _playing(false)
|
|
|
|
, _currentFrame(0)
|
|
|
|
, _startFrame(0)
|
|
|
|
, _endFrame(0)
|
2020-08-26 15:26:06 +08:00
|
|
|
, _loop(false)
|
2019-11-23 20:27:39 +08:00
|
|
|
, _frameEventListener(nullptr)
|
|
|
|
, _lastFrameListener(nullptr)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
ActionTimeline::~ActionTimeline()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ActionTimeline::init()
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::play(std::string name, bool loop)
|
|
|
|
{
|
|
|
|
if (_animationInfos.find(name) == _animationInfos.end())
|
|
|
|
{
|
|
|
|
CCLOG("Can't find animation info for %s", name.c_str());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
AnimationInfo& index = _animationInfos[name];
|
|
|
|
gotoFrameAndPlay(index.startIndex, index.endIndex, loop);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::gotoFrameAndPlay(int startIndex)
|
|
|
|
{
|
|
|
|
gotoFrameAndPlay(startIndex, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::gotoFrameAndPlay(int startIndex, bool loop)
|
|
|
|
{
|
|
|
|
gotoFrameAndPlay(startIndex, _duration, loop);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::gotoFrameAndPlay(int startIndex, int endIndex, bool loop)
|
|
|
|
{
|
|
|
|
gotoFrameAndPlay(startIndex, endIndex, startIndex, loop);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::gotoFrameAndPlay(int startIndex, int endIndex, int currentFrameIndex, bool loop)
|
|
|
|
{
|
|
|
|
_startFrame = startIndex;
|
|
|
|
_endFrame = endIndex;
|
|
|
|
_currentFrame = currentFrameIndex;
|
|
|
|
_loop = loop;
|
|
|
|
_time = _currentFrame*_frameInternal;
|
|
|
|
|
|
|
|
resume();
|
|
|
|
gotoFrame(_currentFrame);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::gotoFrameAndPause(int startIndex)
|
|
|
|
{
|
|
|
|
_startFrame = _currentFrame = startIndex;
|
|
|
|
_time = _currentFrame * _frameInternal;
|
|
|
|
|
|
|
|
pause();
|
|
|
|
gotoFrame(_currentFrame);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::pause()
|
|
|
|
{
|
|
|
|
_playing = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::resume()
|
|
|
|
{
|
|
|
|
_playing = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ActionTimeline::isPlaying() const
|
|
|
|
{
|
|
|
|
return _playing;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::setCurrentFrame(int frameIndex)
|
|
|
|
{
|
|
|
|
if (frameIndex >= _startFrame && frameIndex <= _endFrame)
|
|
|
|
{
|
|
|
|
_currentFrame = frameIndex;
|
|
|
|
_time = _currentFrame*_frameInternal;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
CCLOG("frame index is not between start frame and end frame");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ActionTimeline* ActionTimeline::clone() const
|
|
|
|
{
|
|
|
|
ActionTimeline* newAction = ActionTimeline::create();
|
2020-08-26 15:26:06 +08:00
|
|
|
newAction->_duration = _duration;
|
|
|
|
newAction->_timeSpeed = _timeSpeed;
|
2019-11-23 20:27:39 +08:00
|
|
|
|
2019-11-25 01:35:26 +08:00
|
|
|
for (const auto& timelines : _timelineMap)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
for(auto timeline : timelines.second)
|
|
|
|
{
|
|
|
|
Timeline* newTimeline = timeline->clone();
|
|
|
|
newAction->addTimeline(newTimeline);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-25 01:35:26 +08:00
|
|
|
for(const auto& info : _animationInfos)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
newAction->addAnimationInfo(info.second);
|
|
|
|
}
|
|
|
|
return newAction;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::step(float delta)
|
|
|
|
{
|
|
|
|
if (!_playing || _timelineMap.size() == 0 || _duration == 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_time += delta * _timeSpeed;
|
|
|
|
float deltaCurrFrameTime = std::abs(_time - _currentFrame * _frameInternal);
|
|
|
|
if (deltaCurrFrameTime < _frameInternal)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const float endtoffset = _time - _endFrame * _frameInternal;
|
|
|
|
if (endtoffset < _frameInternal)
|
|
|
|
{
|
|
|
|
_currentFrame = (int)(_time / _frameInternal);
|
|
|
|
stepToFrame(_currentFrame);
|
|
|
|
emitFrameEndCallFuncs(_currentFrame);
|
|
|
|
if (endtoffset >= 0 && _lastFrameListener != nullptr) // last frame
|
|
|
|
_lastFrameListener();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_playing = _loop;
|
|
|
|
if (!_playing)
|
|
|
|
{
|
|
|
|
_time = _endFrame * _frameInternal;
|
|
|
|
if (_currentFrame != _endFrame)
|
|
|
|
{
|
|
|
|
_currentFrame = _endFrame;
|
|
|
|
stepToFrame(_currentFrame);
|
|
|
|
emitFrameEndCallFuncs(_currentFrame);
|
|
|
|
if (_lastFrameListener != nullptr) // last frame
|
|
|
|
_lastFrameListener();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
gotoFrameAndPlay(_startFrame, _endFrame, _loop);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef std::function<void(Node*)> tCallBack;
|
|
|
|
void foreachNodeDescendant(Node* parent, tCallBack callback)
|
|
|
|
{
|
|
|
|
callback(parent);
|
|
|
|
|
|
|
|
auto& children = parent->getChildren();
|
|
|
|
for (auto child : children)
|
|
|
|
{
|
|
|
|
foreachNodeDescendant(child, callback);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::startWithTarget(Node *target)
|
|
|
|
{
|
|
|
|
Action::startWithTarget(target);
|
|
|
|
this->setTag(target->getTag());
|
|
|
|
|
|
|
|
foreachNodeDescendant(target,
|
2019-11-25 01:35:26 +08:00
|
|
|
[this](Node* child)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
ComExtensionData* data = dynamic_cast<ComExtensionData*>(child->getComponent("ComExtensionData"));
|
|
|
|
|
|
|
|
if(data)
|
|
|
|
{
|
|
|
|
int actionTag = data->getActionTag();
|
|
|
|
if(_timelineMap.find(actionTag) != _timelineMap.end())
|
|
|
|
{
|
|
|
|
auto timelines = this->_timelineMap[actionTag];
|
|
|
|
for (auto timeline : timelines)
|
|
|
|
{
|
|
|
|
timeline->setNode(child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::addTimeline(Timeline* timeline)
|
|
|
|
{
|
|
|
|
int tag = timeline->getActionTag();
|
|
|
|
if (_timelineMap.find(tag) == _timelineMap.end())
|
|
|
|
{
|
|
|
|
_timelineMap[tag] = Vector<Timeline*>();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!_timelineMap[tag].contains(timeline))
|
|
|
|
{
|
|
|
|
_timelineList.pushBack(timeline);
|
|
|
|
_timelineMap[tag].pushBack(timeline);
|
|
|
|
timeline->setActionTimeline(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::removeTimeline(Timeline* timeline)
|
|
|
|
{
|
|
|
|
int tag = timeline->getActionTag();
|
|
|
|
if (_timelineMap.find(tag) != _timelineMap.end())
|
|
|
|
{
|
|
|
|
if(_timelineMap[tag].contains(timeline))
|
|
|
|
{
|
|
|
|
_timelineMap[tag].eraseObject(timeline);
|
|
|
|
_timelineList.eraseObject(timeline);
|
|
|
|
timeline->setActionTimeline(nullptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ActionTimeline::addAnimationInfo(const AnimationInfo& animationInfo)
|
|
|
|
{
|
|
|
|
if (_animationInfos.find(animationInfo.name) != _animationInfos.end())
|
|
|
|
{
|
|
|
|
CCLOG("Animation (%s) already exists.", animationInfo.name.c_str());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_animationInfos[animationInfo.name] = animationInfo;
|
|
|
|
addFrameEndCallFunc(animationInfo.endIndex, animationInfo.name, animationInfo.clipEndCallBack);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::removeAnimationInfo(std::string animationName)
|
|
|
|
{
|
|
|
|
auto clipIter = _animationInfos.find(animationName);
|
|
|
|
if (clipIter == _animationInfos.end())
|
|
|
|
{
|
|
|
|
CCLOG("AnimationInfo (%s) not exists.", animationName.c_str());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
removeFrameEndCallFunc((*clipIter).second.endIndex, animationName);
|
|
|
|
_animationInfos.erase(animationName);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ActionTimeline::IsAnimationInfoExists(const std::string& animationName)
|
|
|
|
{
|
|
|
|
return _animationInfos.find(animationName) != _animationInfos.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
const AnimationInfo& ActionTimeline::getAnimationInfo(const std::string &animationName)
|
|
|
|
{
|
|
|
|
return _animationInfos.find(animationName)->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::setAnimationEndCallFunc(const std::string animationName, std::function<void()> func)
|
|
|
|
{
|
|
|
|
auto clipIter = _animationInfos.find(animationName);
|
|
|
|
if (clipIter == _animationInfos.end())
|
|
|
|
{
|
|
|
|
CCLOG("AnimationInfo (%s) not exists.", animationName.c_str());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
clipIter->second.clipEndCallBack = func;
|
|
|
|
addFrameEndCallFunc(clipIter->second.endIndex, animationName, func);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::setFrameEventCallFunc(std::function<void(Frame *)> listener)
|
|
|
|
{
|
|
|
|
_frameEventListener = listener;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::clearFrameEventCallFunc()
|
|
|
|
{
|
|
|
|
_frameEventListener = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::setLastFrameCallFunc(std::function<void()> listener)
|
|
|
|
{
|
|
|
|
_lastFrameListener = listener;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::clearLastFrameCallFunc()
|
|
|
|
{
|
|
|
|
_lastFrameListener = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::emitFrameEvent(Frame* frame)
|
|
|
|
{
|
|
|
|
if(_frameEventListener)
|
|
|
|
{
|
|
|
|
_frameEventListener(frame);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::addFrameEndCallFunc(int frameIndex, const std::string& funcKey, std::function<void()> func)
|
|
|
|
{
|
|
|
|
if (func != nullptr)
|
|
|
|
{
|
|
|
|
_frameEndCallFuncs[frameIndex][funcKey] = func;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::removeFrameEndCallFunc(int frameIndex, const std::string& funcKey)
|
|
|
|
{
|
|
|
|
auto endClipCallsIter = _frameEndCallFuncs.find(frameIndex);
|
|
|
|
if (endClipCallsIter != _frameEndCallFuncs.end())
|
|
|
|
{
|
|
|
|
auto funcIter = (*endClipCallsIter).second.find(funcKey);
|
|
|
|
if (funcIter != (*endClipCallsIter).second.end())
|
|
|
|
(*endClipCallsIter).second.erase(funcKey);
|
|
|
|
if ((*endClipCallsIter).second.empty())
|
|
|
|
_frameEndCallFuncs.erase(endClipCallsIter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::removeFrameEndCallFuncs(int frameIndex)
|
|
|
|
{
|
|
|
|
auto endClipCallsIter = _frameEndCallFuncs.find(frameIndex);
|
|
|
|
if (endClipCallsIter != _frameEndCallFuncs.end())
|
|
|
|
{
|
|
|
|
_frameEndCallFuncs.erase(endClipCallsIter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::clearFrameEndCallFuncs()
|
|
|
|
{
|
|
|
|
_frameEndCallFuncs.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::emitFrameEndCallFuncs(int frameIndex)
|
|
|
|
{
|
|
|
|
auto clipEndCallsIter = _frameEndCallFuncs.find(frameIndex);
|
|
|
|
if (clipEndCallsIter != _frameEndCallFuncs.end())
|
|
|
|
{
|
|
|
|
auto clipEndCalls = (*clipEndCallsIter).second;
|
2019-11-25 01:35:26 +08:00
|
|
|
for (const auto& call : clipEndCalls)
|
2019-11-23 20:27:39 +08:00
|
|
|
(call).second();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::gotoFrame(int frameIndex)
|
|
|
|
{
|
|
|
|
if(_target == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ssize_t size = _timelineList.size();
|
|
|
|
for(ssize_t i = 0; i < size; i++)
|
|
|
|
{
|
|
|
|
_timelineList.at(i)->gotoFrame(frameIndex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::stepToFrame(int frameIndex)
|
|
|
|
{
|
|
|
|
ssize_t size = _timelineList.size();
|
|
|
|
for(ssize_t i = 0; i < size; i++)
|
|
|
|
{
|
|
|
|
_timelineList.at(i)->stepToFrame(frameIndex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::start()
|
|
|
|
{
|
|
|
|
gotoFrameAndPlay(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActionTimeline::stop()
|
|
|
|
{
|
|
|
|
pause();
|
|
|
|
}
|
|
|
|
NS_TIMELINE_END
|