Files
2020-08-24 23:16:53 +07:00

775 lines
26 KiB
JavaScript

/****************************************************************************
Copyright (c) 2008-2010 Ricardo Quesada
Copyright (c) 2011-2012 cocos2d-x.org
Copyright (c) 2013-2014 Chukong Technologies Inc.
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.
****************************************************************************/
(function () {
var _pos = cc.p();
cc.BuilderAnimationManagerDelegate = cc.Class.extend({
completedAnimationSequenceNamed: function (name) {
}
});
cc.BuilderAnimationManager = cc.Class.extend({
_sequences: null,
_nodeSequences: null,
_baseValues: null,
_autoPlaySequenceId: 0,
_rootNode: null,
_owner: null,
_rootContainerSize: null,
_delegate: null,
_runningSequence: null,
_documentOutletNames: null,
_documentOutletNodes: null,
_documentCallbackNames: null,
_documentCallbackNodes: null,
_documentCallbackControlEvents: null,
_documentControllerName: "",
_lastCompletedSequenceName: "",
_keyframeCallbacks: null,
_keyframeCallFuncs: null,
_animationCompleteCallbackFunc: null,
_target: null,
_jsControlled: false,
ctor: function () {
this._rootContainerSize = cc.size(0, 0);
this.init();
},
init: function () {
this._sequences = [];
this._nodeSequences = new cc._Dictionary();
this._baseValues = new cc._Dictionary();
this._documentOutletNames = [];
this._documentOutletNodes = [];
this._documentCallbackNames = [];
this._documentCallbackNodes = [];
this._documentCallbackControlEvents = [];
this._keyframeCallbacks = [];
this._keyframeCallFuncs = {};
return true;
},
getSequences: function () {
return this._sequences;
},
setSequences: function (seqs) {
this._sequences = seqs;
},
getAutoPlaySequenceId: function () {
return this._autoPlaySequenceId;
},
setAutoPlaySequenceId: function (autoPlaySequenceId) {
this._autoPlaySequenceId = autoPlaySequenceId;
},
getRootNode: function () {
return this._rootNode;
},
setRootNode: function (rootNode) {
this._rootNode = rootNode;
},
getOwner: function () {
return this._owner;
},
setOwner: function (owner) {
this._owner = owner;
},
addDocumentCallbackNode: function (node) {
this._documentCallbackNodes.push(node);
},
addDocumentCallbackName: function (name) {
this._documentCallbackNames.push(name);
},
addDocumentCallbackControlEvents: function (controlEvents) {
this._documentCallbackControlEvents.push(controlEvents);
},
addDocumentOutletNode: function (node) {
this._documentOutletNodes.push(node);
},
addDocumentOutletName: function (name) {
this._documentOutletNames.push(name);
},
setDocumentControllerName: function (name) {
this._documentControllerName = name;
},
getDocumentControllerName: function () {
return this._documentControllerName;
},
getDocumentCallbackNames: function () {
return this._documentCallbackNames;
},
getDocumentCallbackNodes: function () {
return this._documentCallbackNodes;
},
getDocumentCallbackControlEvents: function () {
return this._documentCallbackControlEvents;
},
getDocumentOutletNames: function () {
return this._documentOutletNames;
},
getDocumentOutletNodes: function () {
return this._documentOutletNodes;
},
getLastCompletedSequenceName: function () {
return this._lastCompletedSequenceName;
},
getKeyframeCallbacks: function () {
return this._keyframeCallbacks;
},
getRootContainerSize: function () {
return this._rootContainerSize;
},
setRootContainerSize: function (rootContainerSize) {
this._rootContainerSize = cc.size(rootContainerSize.width, rootContainerSize.height);
},
getDelegate: function () {
return this._delegate;
},
setDelegate: function (delegate) {
this._delegate = delegate;
},
getRunningSequenceName: function () {
if (this._runningSequence)
return this._runningSequence.getName();
return null;
},
getContainerSize: function (node) {
if (node)
return node.getContentSize();
else
return this._rootContainerSize;
},
addNode: function (node, seq) {
this._nodeSequences.setObject(seq, node);
},
setBaseValue: function (value, node, propName) {
var props = this._baseValues.objectForKey(node);
if (!props) {
props = new cc._Dictionary();
this._baseValues.setObject(props, node);
}
props.setObject(value, propName);
},
moveAnimationsFromNode: function (fromNode, toNode) {
// Move base values
var locBaseValues = this._baseValues;
var baseValue = locBaseValues.objectForKey(fromNode);
if (baseValue !== null) {
locBaseValues.setObject(baseValue, toNode);
locBaseValues.removeObjectForKey(fromNode);
}
// Move seqs
var locNodeSequences = this._nodeSequences;
var seqs = locNodeSequences.objectForKey(fromNode);
if (seqs != null) {
locNodeSequences.setObject(seqs, toNode);
locNodeSequences.removeObjectForKey(fromNode);
}
},
getActionForCallbackChannel: function (channel) {
var lastKeyframeTime = 0;
var actions = [];
var keyframes = channel.getKeyframes();
var numKeyframes = keyframes.length;
for (var i = 0; i < numKeyframes; ++i) {
var keyframe = keyframes[i];
var timeSinceLastKeyframe = keyframe.getTime() - lastKeyframeTime;
lastKeyframeTime = keyframe.getTime();
if (timeSinceLastKeyframe > 0) {
actions.push(cc.delayTime(timeSinceLastKeyframe));
}
var keyVal = keyframe.getValue();
var selectorName = keyVal[0];
var selectorTarget = keyVal[1];
if (this._jsControlled) {
var callbackName = selectorTarget + ":" + selectorName; //add number to the stream
var callback = this._keyframeCallFuncs[callbackName];
if (callback != null)
actions.push(callback);
} else {
var target;
if (selectorTarget === CCB_TARGETTYPE_DOCUMENTROOT)
target = this._rootNode;
else if (selectorTarget === CCB_TARGETTYPE_OWNER)
target = this._owner;
if (target != null) {
if (selectorName.length > 0) {
var selCallFunc = 0;
if (target.onResolveCCBCCCallFuncSelector != null)
selCallFunc = target.onResolveCCBCCCallFuncSelector(target, selectorName);
if (selCallFunc === 0)
cc.log("Skipping selector '" + selectorName + "' since no CCBSelectorResolver is present.");
else
actions.push(cc.callFunc(selCallFunc, target));
} else {
cc.log("Unexpected empty selector.");
}
}
}
}
if (actions.length < 1)
return null;
return cc.sequence(actions);
},
getActionForSoundChannel: function (channel) {
var lastKeyframeTime = 0;
var actions = [];
var keyframes = channel.getKeyframes();
var numKeyframes = keyframes.length;
for (var i = 0; i < numKeyframes; ++i) {
var keyframe = keyframes[i];
var timeSinceLastKeyframe = keyframe.getTime() - lastKeyframeTime;
lastKeyframeTime = keyframe.getTime();
if (timeSinceLastKeyframe > 0) {
actions.push(cc.delayTime(timeSinceLastKeyframe));
}
var keyVal = keyframe.getValue();
var soundFile = cc.BuilderReader.getResourcePath() + keyVal[0];
var pitch = parseFloat(keyVal[1]), pan = parseFloat(keyVal[2]), gain = parseFloat(keyVal[3]);
actions.push(cc.BuilderSoundEffect.create(soundFile, pitch, pan, gain));
}
if (actions.length < 1)
return null;
return cc.sequence(actions);
},
runAnimationsForSequenceNamed: function (name) {
this.runAnimationsForSequenceIdTweenDuration(this._getSequenceId(name), 0);
},
runAnimationsForSequenceNamedTweenDuration: function (name, tweenDuration) {
this.runAnimationsForSequenceIdTweenDuration(this._getSequenceId(name), tweenDuration);
},
runAnimationsForSequenceIdTweenDuration: function (nSeqId, tweenDuration) {
if (nSeqId === -1)
throw new Error("cc.BuilderAnimationManager.runAnimationsForSequenceIdTweenDuration(): Sequence id should not be -1");
tweenDuration = tweenDuration || 0;
this._rootNode.stopAllActions();
var allKeys = this._nodeSequences.allKeys();
for (var i = 0, len = allKeys.length; i < len; i++) {
var node = allKeys[i];
node.stopAllActions();
var seqs = this._nodeSequences.objectForKey(node);
var seqNodeProps = seqs.objectForKey(nSeqId);
var j;
var seqNodePropNames = [];
if (seqNodeProps) {
var propKeys = seqNodeProps.allKeys();
for (j = 0; j < propKeys.length; j++) {
var propName = propKeys[j];
var seqProp = seqNodeProps.objectForKey(propName);
seqNodePropNames.push(propName);
this._setFirstFrame(node, seqProp, tweenDuration);
this._runAction(node, seqProp, tweenDuration);
}
}
var nodeBaseValues = this._baseValues.objectForKey(node);
if (nodeBaseValues) {
var baseKeys = nodeBaseValues.allKeys();
for (j = 0; j < baseKeys.length; j++) {
var selBaseKey = baseKeys[j];
if (seqNodePropNames.indexOf(selBaseKey) === -1) {
var value = nodeBaseValues.objectForKey(selBaseKey);
if (value != null)
this._setAnimatedProperty(selBaseKey, node, value, tweenDuration);
}
}
}
}
// Make callback at end of sequence
var seq = this._getSequence(nSeqId);
var completeAction = cc.sequence(cc.delayTime(seq.getDuration() + tweenDuration),
cc.callFunc(this._sequenceCompleted, this));
this._rootNode.runAction(completeAction);
// Playback callbacks and sounds
var action;
if (seq.getCallbackChannel()) {
// Build sound actions for channel
action = this.getActionForCallbackChannel(seq.getCallbackChannel());
if (action) {
this._rootNode.runAction(action);
}
}
if (seq.getSoundChannel()) {
// Build sound actions for channel
action = this.getActionForSoundChannel(seq.getSoundChannel());
if (action) {
this._rootNode.runAction(action);
}
}
// Set the running scene
this._runningSequence = this._getSequence(nSeqId);
},
runAnimations: function (name, tweenDuration) {
tweenDuration = tweenDuration || 0;
var nSeqId;
if (cc.isString(name))
nSeqId = this._getSequenceId(name);
else
nSeqId = name;
this.runAnimationsForSequenceIdTweenDuration(nSeqId, tweenDuration);
},
setAnimationCompletedCallback: function (target, callbackFunc) {
this._target = target;
this._animationCompleteCallbackFunc = callbackFunc;
},
setCompletedAnimationCallback: function (target, callbackFunc) {
this.setAnimationCompletedCallback(target, callbackFunc);
},
setCallFunc: function (callFunc, callbackNamed) {
this._keyframeCallFuncs[callbackNamed] = callFunc;
},
debug: function () {
},
_getBaseValue: function (node, propName) {
var props = this._baseValues.objectForKey(node);
if (props)
return props.objectForKey(propName);
return null;
},
_getSequenceId: function (sequenceName) {
var element = null;
var locSequences = this._sequences;
for (var i = 0, len = locSequences.length; i < len; i++) {
element = locSequences[i];
if (element && element.getName() === sequenceName)
return element.getSequenceId();
}
return -1;
},
_getSequence: function (sequenceId) {
var element = null;
var locSequences = this._sequences;
for (var i = 0, len = locSequences.length; i < len; i++) {
element = locSequences[i];
if (element && element.getSequenceId() === sequenceId)
return element;
}
return null;
},
_getAction: function (keyframe0, keyframe1, propName, node) {
var duration = keyframe1.getTime() - (keyframe0 ? keyframe0.getTime() : 0);
var getArr, type, getValueArr, x, y;
if (propName === "rotation") {
return cc.BuilderRotateTo.create(duration, keyframe1.getValue());
} else if (propName === "rotationX") {
return cc.BuilderRotateXTo.create(duration, keyframe1.getValue());
} else if (propName === "rotationY") {
return cc.BuilderRotateYTo.create(duration, keyframe1.getValue());
} else if (propName === "opacity") {
return cc.fadeTo(duration, keyframe1.getValue());
} else if (propName === "color") {
var selColor = keyframe1.getValue();
return cc.tintTo(duration, selColor.r, selColor.g, selColor.b);
} else if (propName === "visible") {
var isVisible = keyframe1.getValue();
if (isVisible) {
return cc.sequence(cc.delayTime(duration), cc.show());
} else {
return cc.sequence(cc.delayTime(duration), cc.hide());
}
} else if (propName === "displayFrame") {
return cc.sequence(cc.delayTime(duration), cc.BuilderSetSpriteFrame.create(keyframe1.getValue()));
} else if (propName === "position") {
getArr = this._getBaseValue(node, propName);
type = getArr[2];
//get relative position
getValueArr = keyframe1.getValue();
x = getValueArr[0];
y = getValueArr[1];
var containerSize = this.getContainerSize(node.getParent());
var absPos = cc.getAbsolutePosition(x, y, type, containerSize, propName);
return cc.moveTo(duration, absPos);
} else if (propName === "scale") {
getArr = this._getBaseValue(node, propName);
type = getArr[2];
//get relative position
getValueArr = keyframe1.getValue();
x = getValueArr[0];
y = getValueArr[1];
if (type === CCB_SCALETYPE_MULTIPLY_RESOLUTION) {
//TODO need to test
var resolutionScale = cc.BuilderReader.getResolutionScale();
x *= resolutionScale;
y *= resolutionScale;
}
return cc.scaleTo(duration, x, y);
} else if (propName === "skew") {
//get relative position
getValueArr = keyframe1.getValue();
x = getValueArr[0];
y = getValueArr[1];
return cc.skewTo(duration, x, y);
} else {
cc.log("BuilderReader: Failed to create animation for property: " + propName);
}
return null;
},
_setAnimatedProperty: function (propName, node, value, tweenDuration) {
if (tweenDuration > 0) {
// Create a fake keyframe to generate the action from
var kf1 = new cc.BuilderKeyframe();
kf1.setValue(value);
kf1.setTime(tweenDuration);
kf1.setEasingType(CCB_KEYFRAME_EASING_LINEAR);
// Animate
var tweenAction = this._getAction(null, kf1, propName, node);
node.runAction(tweenAction);
} else {
// Just set the value
var getArr, nType, x, y;
if (propName === "position") {
getArr = this._getBaseValue(node, propName);
nType = getArr[2];
x = value[0];
y = value[1];
cc.getAbsolutePosition(x, y, nType, this.getContainerSize(node.getParent()), propName, _pos);
node._position.x = _pos.x;
node._position.y = _pos.y;
} else if (propName === "scale") {
getArr = this._getBaseValue(node, propName);
nType = getArr[2];
x = value[0];
y = value[1];
cc.setRelativeScale(node, x, y, nType, propName);
} else if (propName === "skew") {
x = value[0];
y = value[1];
node._skewX = x;
node._skewY = y;
} else {
// [node setValue:value forKey:name];
// TODO only handle rotation, opacity, displayFrame, color
if (propName === "rotation") {
node.setRotation(value);
} else if (propName === "rotationX") {
node._rotationX = value;
} else if (propName === "rotationY") {
node._rotationY = value;
} else if (propName === "opacity") {
node._realOpacity = value;
} else if (propName === "displayFrame") {
node.setSpriteFrame(value);
} else if (propName === "color") {
if (value.r !== 255 || value.g !== 255 || value.b !== 255) {
node.setColor(value);
}
} else if (propName === "visible") {
value = value || false;
node.setVisible(value);
} else {
cc.log("unsupported property name is " + propName);
return;
}
}
node.setNodeDirty();
}
},
_setFirstFrame: function (node, seqProp, tweenDuration) {
var keyframes = seqProp.getKeyframes();
if (keyframes.length === 0) {
// Use base value (no animation)
var baseValue = this._getBaseValue(node, seqProp.getName());
if (!baseValue)
cc.log("cc.BuilderAnimationManager._setFirstFrame(): No baseValue found for property");
this._setAnimatedProperty(seqProp.getName(), node, baseValue, tweenDuration);
} else {
// Use first keyframe
var keyframe = keyframes[0];
this._setAnimatedProperty(seqProp.getName(), node, keyframe.getValue(), tweenDuration);
}
},
_getEaseAction: function (action, easingType, easingOpt) {
if (easingType === CCB_KEYFRAME_EASING_LINEAR || easingType === CCB_KEYFRAME_EASING_INSTANT) {
return action;
} else if (easingType === CCB_KEYFRAME_EASING_CUBIC_IN) {
return action.easing(cc.easeIn(easingOpt));
} else if (easingType === CCB_KEYFRAME_EASING_CUBIC_OUT) {
return action.easing(cc.easeOut(easingOpt));
} else if (easingType === CCB_KEYFRAME_EASING_CUBIC_INOUT) {
return action.easing(cc.easeInOut(easingOpt));
} else if (easingType === CCB_KEYFRAME_EASING_BACK_IN) {
return action.easing(cc.easeBackIn());
} else if (easingType === CCB_KEYFRAME_EASING_BACK_OUT) {
return action.easing(cc.easeBackOut());
} else if (easingType === CCB_KEYFRAME_EASING_BACK_INOUT) {
return action.easing(cc.easeBackInOut());
} else if (easingType === CCB_KEYFRAME_EASING_BOUNCE_IN) {
return action.easing(cc.easeBounceIn());
} else if (easingType === CCB_KEYFRAME_EASING_BOUNCE_OUT) {
return action.easing(cc.easeBounceOut());
} else if (easingType === CCB_KEYFRAME_EASING_BOUNCE_INOUT) {
return action.easing(cc.easeBounceInOut());
} else if (easingType === CCB_KEYFRAME_EASING_ELASTIC_IN) {
return action.easing(cc.easeElasticIn(easingOpt));
} else if (easingType === CCB_KEYFRAME_EASING_ELASTIC_OUT) {
return action.easing(cc.easeElasticOut(easingOpt));
} else if (easingType === CCB_KEYFRAME_EASING_ELASTIC_INOUT) {
return action.easing(cc.easeElasticInOut(easingOpt));
} else {
cc.log("BuilderReader: Unknown easing type " + easingType);
return action;
}
},
_runAction: function (node, seqProp, tweenDuration) {
var keyframes = seqProp.getKeyframes();
var numKeyframes = keyframes.length;
if (numKeyframes > 1) {
// Make an animation!
var actions = [];
var keyframeFirst = keyframes[0];
var timeFirst = keyframeFirst.getTime() + tweenDuration;
if (timeFirst > 0) {
actions.push(cc.delayTime(timeFirst));
}
for (var i = 0; i < numKeyframes - 1; ++i) {
var kf0 = keyframes[i];
var kf1 = keyframes[(i + 1)];
var action = this._getAction(kf0, kf1, seqProp.getName(), node);
if (action) {
// Apply easing
action = this._getEaseAction(action, kf0.getEasingType(), kf0.getEasingOpt());
actions.push(action);
}
}
node.runAction(cc.sequence(actions));
}
},
_sequenceCompleted: function () {
var locRunningSequence = this._runningSequence;
var locRunningName = locRunningSequence.getName();
if (this._lastCompletedSequenceName != locRunningSequence.getName()) {
this._lastCompletedSequenceName = locRunningSequence.getName();
}
var nextSeqId = locRunningSequence.getChainedSequenceId();
this._runningSequence = null;
if (nextSeqId !== -1)
this.runAnimations(nextSeqId, 0);
if (this._delegate)
this._delegate.completedAnimationSequenceNamed(locRunningName);
if (this._target && this._animationCompleteCallbackFunc) {
this._animationCompleteCallbackFunc.call(this._target);
}
}
});
cc.BuilderSetSpriteFrame = cc.ActionInstant.extend({
_spriteFrame: null,
initWithSpriteFrame: function (spriteFrame) {
this._spriteFrame = spriteFrame;
return true;
},
update: function (time) {
this.target.setSpriteFrame(this._spriteFrame);
}
});
cc.BuilderSetSpriteFrame.create = function (spriteFrame) {
var ret = new cc.BuilderSetSpriteFrame();
if (ret) {
if (ret.initWithSpriteFrame(spriteFrame))
return ret;
}
return null;
};
//
// cc.BuilderRotateTo
//
cc.BuilderRotateTo = cc.ActionInterval.extend({
_startAngle: 0,
_dstAngle: 0,
_diffAngle: 0,
initWithDuration: function (duration, angle) {
if (cc.ActionInterval.prototype.initWithDuration.call(this, duration)) {
this._dstAngle = angle;
return true;
} else {
return false;
}
},
update: function (time) {
this.target.setRotation(this._startAngle + (this._diffAngle * time));
},
startWithTarget: function (node) {
cc.ActionInterval.prototype.startWithTarget.call(this, node);
this._startAngle = this.target.getRotation();
this._diffAngle = this._dstAngle - this._startAngle;
}
});
cc.BuilderRotateTo.create = function (duration, angle) {
var ret = new cc.BuilderRotateTo();
if (ret) {
if (ret.initWithDuration(duration, angle))
return ret;
}
return null;
};
//
// cc.BuilderRotateXTo
//
cc.BuilderRotateXTo = cc.ActionInterval.extend({
// TODO: rotationX is not implemented in HTML5
});
cc.BuilderRotateXTo.create = function (duration, angle) {
throw new Error("rotationX has not been implemented in cocos2d-html5");
};
//
// cc.BuilderRotateYTo
//
cc.BuilderRotateYTo = cc.ActionInterval.extend({
// TODO: rotationX is not implemented in HTML5
});
cc.BuilderRotateYTo.create = function (duration, angle) {
throw new Error("rotationY has not been implemented in cocos2d-html5");
};
//
// cc.BuilderSoundEffect
//
cc.BuilderSoundEffect = cc.ActionInstant.extend({
init: function (file) {
this._file = file;
return true;
},
update: function (dt) {
cc.audioEngine.playEffect(this._file);
}
});
cc.BuilderSoundEffect.create = function (file, pitch, pan, gain) {
var ret = new cc.BuilderSoundEffect();
if (ret && ret.init(file)) {
return ret;
}
return null;
};
})();