com.brashmonkey.spriter.TweenedAnimation Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of overlap2d-runtime-libgdx Show documentation
Show all versions of overlap2d-runtime-libgdx Show documentation
overlap2d-runtime-libgdx provides functionality to load, manipulate and render scenes generated by Overlap2D.
The newest version!
package com.brashmonkey.spriter;
import com.brashmonkey.spriter.Mainline.Key.BoneRef;
import com.brashmonkey.spriter.Mainline.Key.ObjectRef;
import com.brashmonkey.spriter.Timeline.Key.Bone;
import com.brashmonkey.spriter.Timeline.Key.Object;
/**
* A tweened animation is responsible for updating itself based on two given animations.
* The values of the two given animations will get interpolated and save in this animation.
* When tweening two animations, you have to make sure that they have the same structure.
* The best result is achieved if bones of two different animations are named in the same way.
* There are still issues with sprites, which are hard to resolve since Spriter does not save them in a useful order or naming convention.
* @author Trixt0r
*
*/
public class TweenedAnimation extends Animation{
/**
* The weight of the interpolation. 0.5f is the default value.
* Values closer to 0.0f mean the first animation will have more influence.
*/
public float weight = .5f;
/**
* Indicates when a sprite should be switched form the first animation object to the second one.
* A value closer to 0.0f means that the sprites of the second animation will be drawn.
*/
public float spriteThreshold = .5f;
/**
* The curve which will tween the animations.
* The default type of the curve is {@link Curve.Type#Linear}.
*/
public final Curve curve;
/**
* The entity the animations have be part of.
* Animations of two different entities can not be tweened.
*/
public final Entity entity;
private Animation anim1, anim2;
/**
* The base animation an object or bone will get if it will not be tweened.
*/
public Animation baseAnimation;
BoneRef base = null;
/**
* Indicates whether to tween sprites or not. Default value is false
.
* Tweening sprites should be only enabled if they have exactly the same structure.
* If all animations are bone based and sprites only change their references it is not recommended to tween sprites.
*/
public boolean tweenSprites = false;
/**
* Creates a tweened animation based on the given entity.
* @param entity the entity animations have to be part of
*/
public TweenedAnimation(Entity entity) {
super(new Mainline(0), -1, "__interpolatedAnimation__", 0, true, entity.getAnimationWithMostTimelines().timelines());
this.entity = entity;
this.curve = new Curve();
this.setUpTimelines();
}
/**
* Returns the current mainline key.
* @return the mainline key
*/
public Mainline.Key getCurrentKey(){
return this.currentKey;
}
@Override
public void update(int time, Bone root){
super.currentKey = onFirstMainLine() ? anim1.currentKey: anim2.currentKey;
for(Timeline.Key timelineKey: this.unmappedTweenedKeys)
timelineKey.active = false;
if(base != null){//TODO: Sprites not working properly because of different timeline naming
Animation currentAnim = onFirstMainLine() ? anim1: anim2;
Animation baseAnim = baseAnimation == null ? (onFirstMainLine() ? anim1:anim2) : baseAnimation;
for(BoneRef ref: currentKey.boneRefs){
Timeline timeline = baseAnim.getSimilarTimeline(currentAnim.getTimeline(ref.timeline));
if(timeline == null) continue;
Timeline.Key key, mappedKey;
key = baseAnim.tweenedKeys[timeline.id];
mappedKey = baseAnim.unmappedTweenedKeys[timeline.id];
this.tweenedKeys[ref.timeline].active = key.active;
this.tweenedKeys[ref.timeline].object().set(key.object());
this.unmappedTweenedKeys[ref.timeline].active = mappedKey.active;
this.unmapTimelineObject(ref.timeline, false,(ref.parent != null) ?
this.unmappedTweenedKeys[ref.parent.timeline].object(): root);
}
/*for(ObjectRef ref: baseAnim.currentKey.objectRefs){
Timeline timeline = baseAnim.getTimeline(ref.timeline);//getSimilarTimeline(ref, tempTimelines);
if(timeline != null){
//tempTimelines.addLast(timeline);
Timeline.Key key = baseAnim.tweenedKeys[timeline.id];
Timeline.Key mappedKey = baseAnim.mappedTweenedKeys[timeline.id];
Object obj = (Object) key.object();
this.tweenedKeys[ref.timeline].active = key.active;
((Object)this.tweenedKeys[ref.timeline].object()).set(obj);
this.mappedTweenedKeys[ref.timeline].active = mappedKey.active;
this.unmapTimelineObject(ref.timeline, true,(ref.parent != null) ?
this.mappedTweenedKeys[ref.parent.timeline].object(): root);
}
}*/
//tempTimelines.clear();
}
this.tweenBoneRefs(base, root);
for(ObjectRef ref: super.currentKey.objectRefs){
//if(ref.parent == base)
this.update(ref, root, 0);
}
}
private void tweenBoneRefs(BoneRef base, Bone root){
int startIndex = base == null ? -1 : base.id-1;
int length = super.currentKey.boneRefs.length;
for(int i = startIndex+1; i < length; i++){
BoneRef ref = currentKey.boneRefs[i];
if(base == ref || ref.parent == base) this.update(ref, root, 0);
if(base == ref.parent) this.tweenBoneRefs(ref, root);
}
}
@Override
protected void update(BoneRef ref, Bone root, int time){
boolean isObject = ref instanceof ObjectRef;
//Tween bone/object
Bone bone1 = null, bone2 = null, tweenTarget = null;
Timeline t1 = onFirstMainLine() ? anim1.getTimeline(ref.timeline) : anim1.getSimilarTimeline(anim2.getTimeline(ref.timeline));
Timeline t2 = onFirstMainLine() ? anim2.getSimilarTimeline(t1) : anim2.getTimeline(ref.timeline);
Timeline targetTimeline = super.getTimeline(onFirstMainLine() ? t1.id:t2.id);
if(t1 != null) bone1 = anim1.tweenedKeys[t1.id].object();
if(t2 != null) bone2 = anim2.tweenedKeys[t2.id].object();
if(targetTimeline != null) tweenTarget = this.tweenedKeys[targetTimeline.id].object();
if(isObject && (t2 == null || !tweenSprites)){
if(!onFirstMainLine()) bone1 = bone2;
else bone2 = bone1;
}
if(bone2 != null && tweenTarget != null && bone1 != null){
if(isObject) this.tweenObject((Object)bone1, (Object)bone2, (Object)tweenTarget, this.weight, this.curve);
else this.tweenBone(bone1, bone2, tweenTarget, this.weight, this.curve);
this.unmappedTweenedKeys[targetTimeline.id].active = true;
}
//Transform the bone relative to the parent bone or the root
if(this.unmappedTweenedKeys[ref.timeline].active){
this.unmapTimelineObject(targetTimeline.id, isObject,(ref.parent != null) ?
this.unmappedTweenedKeys[ref.parent.timeline].object(): root);
}
}
private void tweenBone(Bone bone1, Bone bone2, Bone target, float t, Curve curve){
target.angle = curve.tweenAngle(bone1.angle, bone2.angle, t);
curve.tweenPoint(bone1.position, bone2.position, t, target.position);
curve.tweenPoint(bone1.scale, bone2.scale, t, target.scale);
curve.tweenPoint(bone1.pivot, bone2.pivot, t, target.pivot);
}
private void tweenObject(Object object1, Object object2, Object target, float t, Curve curve){
this.tweenBone(object1, object2, target, t, curve);
target.alpha = curve.tweenAngle(object1.alpha, object2.alpha, t);
target.ref.set(object1.ref);
}
/**
* Returns whether the current mainline key is the one from the first animation or from the second one.
* @return true
if the mainline key is the one from the first animation
*/
public boolean onFirstMainLine(){
return this.weight < this.spriteThreshold;
}
private void setUpTimelines(){
Animation maxAnim = this.entity.getAnimationWithMostTimelines();
int max = maxAnim.timelines();
for(int i = 0; i < max; i++){
Timeline t = new Timeline(i, maxAnim.getTimeline(i).name, maxAnim.getTimeline(i).objectInfo, 1);
addTimeline(t);
}
prepare();
}
/**
* Sets the animations to tween.
* @param animation1 the first animation
* @param animation2 the second animation
* @throws SpriterException if {@link #entity} does not contain one of the given animations.
*/
public void setAnimations(Animation animation1, Animation animation2){
boolean areInterpolated = animation1 instanceof TweenedAnimation || animation2 instanceof TweenedAnimation;
if(animation1 == anim1 && animation2 == anim2) return;
if((!this.entity.containsAnimation(animation1) || !this.entity.containsAnimation(animation2)) && !areInterpolated)
throw new SpriterException("Both animations have to be part of the same entity!");
this.anim1 = animation1;
this.anim2 = animation2;
}
/**
* Returns the first animation.
* @return the first animation
*/
public Animation getFirstAnimation(){
return this.anim1;
}
/**
* Returns the second animation.
* @return the second animation
*/
public Animation getSecondAnimation(){
return this.anim2;
}
}