/**
   * Adds an animation to be played after the current or last queued animation for a track. If the
   * track is empty, it is equivalent to calling {@link #setAnimation(int, Animation, boolean)}.
   *
   * @param delay Seconds to begin this animation after the start of the previous animation. May be
   *     <= 0 to use the animation duration of the previous track minus any mix duration plus the
   *     <code>delay</code>.
   * @return A track entry to allow further customization of animation playback. References to the
   *     track entry must not be kept after the {@link AnimationStateListener#dispose(TrackEntry)}
   *     event occurs.
   */
  public TrackEntry addAnimation(int trackIndex, Animation animation, boolean loop, float delay) {
    if (animation == null) throw new IllegalArgumentException("animation cannot be null.");

    TrackEntry last = expandToIndex(trackIndex);
    if (last != null) {
      while (last.next != null) last = last.next;
    }

    TrackEntry entry = trackEntry(trackIndex, animation, loop, last);

    if (last == null) {
      setCurrent(trackIndex, entry, true);
      queue.drain();
    } else {
      last.next = entry;
      if (delay <= 0) {
        float duration = last.animationEnd - last.animationStart;
        if (duration != 0)
          delay +=
              duration * (1 + (int) (last.trackTime / duration))
                  - data.getMix(last.animation, animation);
        else delay = 0;
      }
    }

    entry.delay = delay;
    return entry;
  }
 /**
  * Adds an empty animation to be played after the current or last queued animation for a track,
  * and sets the track entry's {@link TrackEntry#getMixDuration()}. If the track is empty, it is
  * equivalent to calling {@link #setEmptyAnimation(int, float)}.
  *
  * @param delay Seconds to begin this animation after the start of the previous animation. May be
  *     <= 0 to use the animation duration of the previous track minus any mix duration plus <code>
  *     delay</code>.
  * @return A track entry to allow further customization of animation playback. References to the
  *     track entry must not be kept after the {@link AnimationStateListener#dispose(TrackEntry)}
  *     event occurs.
  */
 public TrackEntry addEmptyAnimation(int trackIndex, float mixDuration, float delay) {
   if (delay <= 0) delay -= mixDuration;
   TrackEntry entry = addAnimation(trackIndex, emptyAnimation, false, delay);
   entry.mixDuration = mixDuration;
   entry.trackEnd = mixDuration;
   return entry;
 }
  public void update(float delta) {
    delta *= timeScale;
    for (int i = 0; i < tracks.size; i++) {
      TrackEntry current = tracks.get(i);
      if (current == null) continue;

      float trackDelta = delta * current.timeScale;
      float time = current.time + trackDelta;
      float endTime = current.endTime;

      current.time = time;
      if (current.previous != null) {
        current.previous.time += trackDelta;
        current.mixTime += trackDelta;
      }

      // Check if completed the animation or a loop iteration.
      if (current.loop
          ? (current.lastTime % endTime > time % endTime)
          : (current.lastTime < endTime && time >= endTime)) {
        int count = (int) (time / endTime);
        if (current.listener != null) current.listener.complete(i, count);
        for (int ii = 0, nn = listeners.size; ii < nn; ii++) listeners.get(ii).complete(i, count);
      }

      TrackEntry next = current.next;
      if (next != null) {
        if (time - trackDelta > next.delay) setCurrent(i, next);
      } else {
        // End non-looping animation when it reaches its end time and there is no next entry.
        if (!current.loop && current.lastTime >= current.endTime) clearTrack(i);
      }
    }
  }
  /**
   * Poses the skeleton using the track entry animations. There are no side effects other than
   * invoking listeners, so the animation state can be applied to multiple skeletons to pose them
   * identically.
   */
  public void apply(Skeleton skeleton) {
    if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null.");
    if (animationsChanged) animationsChanged();

    Array<Event> events = this.events;

    for (int i = 0, n = tracks.size; i < n; i++) {
      TrackEntry current = tracks.get(i);
      if (current == null || current.delay > 0) continue;

      // Apply mixing from entries first.
      float mix = current.alpha;
      if (current.mixingFrom != null) mix *= applyMixingFrom(current, skeleton);
      else if (current.trackTime >= current.trackEnd) //
      mix = 0; // Set to setup pose the last time the entry will be applied.

      // Apply current entry.
      float animationLast = current.animationLast, animationTime = current.getAnimationTime();
      int timelineCount = current.animation.timelines.size;
      Object[] timelines = current.animation.timelines.items;
      if (mix == 1) {
        for (int ii = 0; ii < timelineCount; ii++)
          ((Timeline) timelines[ii])
              .apply(skeleton, animationLast, animationTime, events, 1, true, false);
      } else {
        boolean firstFrame = current.timelinesRotation.size == 0;
        if (firstFrame) current.timelinesRotation.setSize(timelineCount << 1);
        float[] timelinesRotation = current.timelinesRotation.items;

        boolean[] timelinesFirst = current.timelinesFirst.items;
        for (int ii = 0; ii < timelineCount; ii++) {
          Timeline timeline = (Timeline) timelines[ii];
          if (timeline instanceof RotateTimeline) {
            applyRotateTimeline(
                timeline,
                skeleton,
                animationTime,
                mix,
                timelinesFirst[ii],
                timelinesRotation,
                ii << 1,
                firstFrame);
          } else
            timeline.apply(
                skeleton, animationLast, animationTime, events, mix, timelinesFirst[ii], false);
        }
      }
      queueEvents(current, animationTime);
      events.clear();
      current.nextAnimationLast = animationTime;
      current.nextTrackLast = current.trackTime;
    }

    queue.drain();
  }
 public String toString() {
   StringBuilder buffer = new StringBuilder(64);
   for (int i = 0, n = tracks.size; i < n; i++) {
     TrackEntry entry = tracks.get(i);
     if (entry == null) continue;
     if (buffer.length() > 0) buffer.append(", ");
     buffer.append(entry.toString());
   }
   if (buffer.length() == 0) return "<none>";
   return buffer.toString();
 }
  /** Set the current animation. Any queued animations are cleared. */
  public TrackEntry setAnimation(int trackIndex, Animation animation, boolean loop) {
    TrackEntry current = expandToIndex(trackIndex);
    if (current != null) freeAll(current.next);

    TrackEntry entry = Pools.obtain(TrackEntry.class);
    entry.animation = animation;
    entry.loop = loop;
    entry.endTime = animation.getDuration();
    setCurrent(trackIndex, entry);
    return entry;
  }
  private float applyMixingFrom(TrackEntry entry, Skeleton skeleton) {
    TrackEntry from = entry.mixingFrom;
    if (from.mixingFrom != null) applyMixingFrom(from, skeleton);

    float mix;
    if (entry.mixDuration == 0) // Single frame mix to undo mixingFrom changes.
    mix = 1;
    else {
      mix = entry.mixTime / entry.mixDuration;
      if (mix > 1) mix = 1;
    }

    Array<Event> events = mix < from.eventThreshold ? this.events : null;
    boolean attachments = mix < from.attachmentThreshold, drawOrder = mix < from.drawOrderThreshold;
    float animationLast = from.animationLast, animationTime = from.getAnimationTime();
    int timelineCount = from.animation.timelines.size;
    Object[] timelines = from.animation.timelines.items;
    boolean[] timelinesFirst = from.timelinesFirst.items;
    float alpha = from.alpha * entry.mixAlpha * (1 - mix);

    boolean firstFrame = from.timelinesRotation.size == 0;
    if (firstFrame) from.timelinesRotation.setSize(timelineCount << 1);
    float[] timelinesRotation = from.timelinesRotation.items;

    for (int i = 0; i < timelineCount; i++) {
      Timeline timeline = (Timeline) timelines[i];
      boolean setupPose = timelinesFirst[i];
      if (timeline instanceof RotateTimeline)
        applyRotateTimeline(
            timeline,
            skeleton,
            animationTime,
            alpha,
            setupPose,
            timelinesRotation,
            i << 1,
            firstFrame);
      else {
        if (!setupPose) {
          if (!attachments && timeline instanceof AttachmentTimeline) continue;
          if (!drawOrder && timeline instanceof DrawOrderTimeline) continue;
        }
        timeline.apply(skeleton, animationLast, animationTime, events, alpha, setupPose, true);
      }
    }

    if (entry.mixDuration > 0) queueEvents(from, animationTime);
    this.events.clear();
    from.nextAnimationLast = animationTime;
    from.nextTrackLast = from.trackTime;

    return mix;
  }
  public void apply(Skeleton skeleton) {
    Array<Event> events = this.events;
    int listenerCount = listeners.size;

    for (int i = 0; i < tracks.size; i++) {
      TrackEntry current = tracks.get(i);
      if (current == null) continue;

      events.size = 0;

      float time = current.time;
      float lastTime = current.lastTime;
      float endTime = current.endTime;
      boolean loop = current.loop;
      if (!loop && time > endTime) time = endTime;

      TrackEntry previous = current.previous;
      if (previous == null)
        current.animation.mix(skeleton, lastTime, time, loop, events, current.mix);
      else {
        float previousTime = previous.time;
        if (!previous.loop && previousTime > previous.endTime) previousTime = previous.endTime;
        previous.animation.apply(skeleton, previousTime, previousTime, previous.loop, null);

        float alpha = current.mixTime / current.mixDuration * current.mix;
        if (alpha >= 1) {
          alpha = 1;
          trackEntryPool.free(previous);
          current.previous = null;
        }
        current.animation.mix(skeleton, lastTime, time, loop, events, alpha);
      }

      for (int ii = 0, nn = events.size; ii < nn; ii++) {
        Event event = events.get(ii);
        if (current.listener != null) current.listener.event(i, event);
        for (int iii = 0; iii < listenerCount; iii++) listeners.get(iii).event(i, event);
      }

      // Check if completed the animation or a loop iteration.
      if (loop ? (lastTime % endTime > time % endTime) : (lastTime < endTime && time >= endTime)) {
        int count = (int) (time / endTime);
        if (current.listener != null) current.listener.complete(i, count);
        for (int ii = 0, nn = listeners.size; ii < nn; ii++) listeners.get(ii).complete(i, count);
      }

      current.lastTime = current.time;
    }
  }
 private void disposeNext(TrackEntry entry) {
   TrackEntry next = entry.next;
   while (next != null) {
     queue.dispose(next);
     next = next.next;
   }
   entry.next = null;
 }
  private void setCurrent(int index, TrackEntry current, boolean interrupt) {
    TrackEntry from = expandToIndex(index);
    tracks.set(index, current);

    if (from != null) {
      if (interrupt) queue.interrupt(from);
      current.mixingFrom = from;
      current.mixTime = 0;

      from.timelinesRotation.clear(); // Reset rotation for mixing out, in case entry was mixed in.

      // If not completely mixed in, set mixAlpha so mixing out happens from current mix to zero.
      if (from.mixingFrom != null && from.mixDuration > 0)
        current.mixAlpha *= Math.min(from.mixTime / from.mixDuration, 1);
    }

    queue.start(current);
  }
  private void setCurrent(int index, TrackEntry entry) {
    TrackEntry current = expandToIndex(index);
    if (current != null) {
      TrackEntry previous = current.previous;
      current.previous = null;

      if (current.listener != null) current.listener.end(index);
      for (int i = 0, n = listeners.size; i < n; i++) listeners.get(i).end(index);

      entry.mixDuration = data.getMix(current.animation, entry.animation);
      if (entry.mixDuration > 0) {
        entry.mixTime = 0;
        // If a mix is in progress, mix from the closest animation.
        if (previous != null && current.mixTime / current.mixDuration < 0.5f) {
          entry.previous = previous;
          previous = current;
        } else entry.previous = current;
      } else trackEntryPool.free(current);

      if (previous != null) trackEntryPool.free(previous);
    }

    tracks.set(index, entry);

    if (entry.listener != null) entry.listener.start(index);
    for (int i = 0, n = listeners.size; i < n; i++) listeners.get(i).start(index);
  }
  /**
   * Removes all animations from the track, leaving skeletons in their previous pose.
   *
   * <p>It may be desired to use {@link AnimationState#setEmptyAnimation(int, float)} to mix the
   * skeletons back to the setup pose, rather than leaving them in their previous pose.
   */
  public void clearTrack(int trackIndex) {
    if (trackIndex >= tracks.size) return;
    TrackEntry current = tracks.get(trackIndex);
    if (current == null) return;

    queue.end(current);

    disposeNext(current);

    TrackEntry entry = current;
    while (true) {
      TrackEntry from = entry.mixingFrom;
      if (from == null) break;
      queue.end(from);
      entry.mixingFrom = null;
      entry = from;
    }

    tracks.set(current.trackIndex, null);

    queue.drain();
  }
示例#13
0
  public void apply(Skeleton skeleton) {
    Array<Event> events = this.events;
    int listenerCount = listeners.size;

    for (int i = 0; i < tracks.size; i++) {
      TrackEntry current = tracks.get(i);
      if (current == null) continue;

      events.size = 0;

      float time = current.time;
      boolean loop = current.loop;
      if (!loop && time > current.endTime) time = current.endTime;

      TrackEntry previous = current.previous;
      if (previous == null) current.animation.apply(skeleton, current.lastTime, time, loop, events);
      else {
        float previousTime = previous.time;
        if (!previous.loop && previousTime > previous.endTime) previousTime = previous.endTime;
        previous.animation.apply(skeleton, previousTime, previousTime, previous.loop, null);

        float alpha = current.mixTime / current.mixDuration;
        if (alpha >= 1) {
          alpha = 1;
          Pools.free(previous);
          current.previous = null;
        }
        current.animation.mix(skeleton, current.lastTime, time, loop, events, alpha);
      }

      for (int ii = 0, nn = events.size; ii < nn; ii++) {
        Event event = events.get(ii);
        if (current.listener != null) current.listener.event(i, event);
        for (int iii = 0; iii < listenerCount; iii++) listeners.get(iii).event(i, event);
      }

      current.lastTime = current.time;
    }
  }
示例#14
0
  private void setCurrent(int index, TrackEntry entry) {
    TrackEntry current = expandToIndex(index);
    if (current != null) {
      if (current.previous != null) {
        Pools.free(current.previous);
        current.previous = null;
      }

      if (current.listener != null) current.listener.end(index);
      for (int i = 0, n = listeners.size; i < n; i++) listeners.get(i).end(index);

      entry.mixDuration = data.getMix(current.animation, entry.animation);
      if (entry.mixDuration > 0) {
        entry.mixTime = 0;
        entry.previous = current;
      } else Pools.free(current);
    }

    tracks.set(index, entry);

    if (entry.listener != null) entry.listener.start(index);
    for (int i = 0, n = listeners.size; i < n; i++) listeners.get(i).start(index);
  }
  public void update(float delta) {
    delta *= timeScale;
    for (int i = 0; i < tracks.size; i++) {
      TrackEntry current = tracks.get(i);
      if (current == null) continue;

      current.time += delta * current.timeScale;
      if (current.previous != null) {
        float previousDelta = delta * current.previous.timeScale;
        current.previous.time += previousDelta;
        current.mixTime += previousDelta;
      }

      TrackEntry next = current.next;
      if (next != null) {
        next.time = current.lastTime - next.delay;
        if (next.time >= 0) setCurrent(i, next);
      } else {
        // End non-looping animation when it reaches its end time and there is no next entry.
        if (!current.loop && current.lastTime >= current.endTime) clearTrack(i);
      }
    }
  }
示例#16
0
  /**
   * Adds an animation to be played delay seconds after the current or last queued animation.
   *
   * @param delay May be <= 0 to use duration of previous animation minus any mix duration plus the
   *     negative delay.
   */
  public TrackEntry addAnimation(int trackIndex, Animation animation, boolean loop, float delay) {
    TrackEntry entry = Pools.obtain(TrackEntry.class);
    entry.animation = animation;
    entry.loop = loop;
    entry.time = 0;
    entry.endTime = animation.getDuration();

    TrackEntry last = expandToIndex(trackIndex);
    if (last != null) {
      while (last.next != null) last = last.next;
      last.next = entry;
    } else tracks.set(trackIndex, entry);

    if (delay <= 0) {
      if (last != null) delay += last.endTime - data.getMix(last.animation, animation);
      else delay = 0;
    }
    entry.delay = delay;

    return entry;
  }
  private void updateMixingFrom(TrackEntry entry, float delta) {
    TrackEntry from = entry.mixingFrom;
    if (from == null) return;

    updateMixingFrom(from, delta);

    if (entry.mixTime >= entry.mixDuration && from.mixingFrom == null && entry.mixTime > 0) {
      entry.mixingFrom = null;
      queue.end(from);
      return;
    }

    from.animationLast = from.nextAnimationLast;
    from.trackLast = from.nextTrackLast;
    from.trackTime += delta * from.timeScale;
    entry.mixTime += delta * entry.timeScale;
  }
  /**
   * Increments each track entry {@link TrackEntry#getTrackTime()}, setting queued animations as
   * current if needed.
   */
  public void update(float delta) {
    delta *= timeScale;
    for (int i = 0, n = tracks.size; i < n; i++) {
      TrackEntry current = tracks.get(i);
      if (current == null) continue;

      current.animationLast = current.nextAnimationLast;
      current.trackLast = current.nextTrackLast;

      float currentDelta = delta * current.timeScale;

      if (current.delay > 0) {
        current.delay -= currentDelta;
        if (current.delay > 0) continue;
        currentDelta = -current.delay;
        current.delay = 0;
      }

      TrackEntry next = current.next;
      if (next != null) {
        // When the next entry's delay is passed, change to the next entry, preserving leftover
        // time.
        float nextTime = current.trackLast - next.delay;
        if (nextTime >= 0) {
          next.delay = 0;
          next.trackTime = nextTime + delta * next.timeScale;
          current.trackTime += currentDelta;
          setCurrent(i, next, true);
          while (next.mixingFrom != null) {
            next.mixTime += currentDelta;
            next = next.mixingFrom;
          }
          continue;
        }
      } else {
        // Clear the track when there is no next entry, the track end time is reached, and there is
        // no mixingFrom.
        if (current.trackLast >= current.trackEnd && current.mixingFrom == null) {
          tracks.set(i, null);
          queue.end(current);
          disposeNext(current);
          continue;
        }
      }
      updateMixingFrom(current, delta);

      current.trackTime += currentDelta;
    }

    queue.drain();
  }
 /**
  * Sets an empty animation for a track, discarding any queued animations, and sets the track
  * entry's {@link TrackEntry#getMixDuration()}.
  *
  * <p>Mixing out is done by setting an empty animation. A mix duration of 0 still mixes out over
  * one frame.
  *
  * <p>To mix in, first set an empty animation and add an animation using {@link #addAnimation(int,
  * Animation, boolean, float)}, then set the {@link TrackEntry#setMixDuration(float)} on the
  * returned track entry.
  */
 public TrackEntry setEmptyAnimation(int trackIndex, float mixDuration) {
   TrackEntry entry = setAnimation(trackIndex, emptyAnimation, false);
   entry.mixDuration = mixDuration;
   entry.trackEnd = mixDuration;
   return entry;
 }
  /** @param last May be null. */
  private TrackEntry trackEntry(
      int trackIndex, Animation animation, boolean loop, TrackEntry last) {
    TrackEntry entry = trackEntryPool.obtain();
    entry.trackIndex = trackIndex;
    entry.animation = animation;
    entry.loop = loop;

    entry.eventThreshold = 0;
    entry.attachmentThreshold = 0;
    entry.drawOrderThreshold = 0;

    entry.animationStart = 0;
    entry.animationEnd = animation.getDuration();
    entry.animationLast = -1;
    entry.nextAnimationLast = -1;

    entry.delay = 0;
    entry.trackTime = 0;
    entry.trackLast = -1;
    entry.nextTrackLast = -1;
    entry.trackEnd = Float.MAX_VALUE;
    entry.timeScale = 1;

    entry.alpha = 1;
    entry.mixAlpha = 1;
    entry.mixTime = 0;
    entry.mixDuration = last == null ? 0 : data.getMix(last.animation, animation);
    return entry;
  }