Android Open Source - DolphinOES Sprite Sequence Drawer






From Project

Back to project page DolphinOES.

License

The source code is released under:

Apache License

If you think the Android project DolphinOES listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*******************************************************************************
 * Copyright 2015 See AUTHORS file.//from  w w w . j a v  a 2 s . com
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package com.sidereal.dolphinoes.behaviors.renderer.spritesequence;

import java.util.ArrayList;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.badlogic.gdx.utils.ObjectMap;
import com.sidereal.dolphinoes.architecture.AbstractEvent;
import com.sidereal.dolphinoes.architecture.DolphinOES;
import com.sidereal.dolphinoes.behaviors.renderer.Drawer;
import com.sidereal.dolphinoes.behaviors.renderer.Renderer;
import com.sidereal.dolphinoes.behaviors.renderer.scml.SCMLDrawer;

/** ImageConstruct used for rendering sprites in a sequence.
 * <p>
 * Gives the ability to change the sequence to play, as well as adding a
 * {@link SpriteSequencePreference} instance to an order/sequence of sprites,
 * customising size, position and animation duration.
 * <p>
 * The class calculates the time to allocate to individual images, changing the
 * images automatically.
 * <p>
 * Transparency, rotation and tint can also be applied to images, however they
 * persist when changing to different sequences.
 * 
 * @see {@link SCMLDrawer} for real animations from Spriter files,
 *      {@link SpriteSequenceDrawer} is only used made to counter the use of
 *      Spriter but is more difficult to work with and more memory-expensive.
 * @see {@link SpriteSequenceBuilder} for easy building of a
 *      {@link SpriteSequenceDrawer} instance.
 * 
 * @author Claudiu Bele */
public class SpriteSequenceDrawer extends Drawer
{

  // region fields

  /** List of sprite Paths that we currently iterate over */
  public ArrayList<String> spritePaths;

  /** Map containing sprite paths, retrievable by name. For example you can
   * have 3 lists of file paths, one for goin down, one for going up, one for
   * attacking.
   * <p>
   * You can get the one for going down and set the {@link #spritePaths} to
   * it. */
  public ObjectMap<String, ArrayList<String>> sequenceFilepaths;

  /** Map containing configurations mapped to a sequence. Added in
   * {@link #addPreferences(String, SpriteSequencePreference)} only if the
   * name of the sequence we attach it to is found in
   * {@link #sequenceFilepaths}. */
  public ObjectMap<String, SpriteSequencePreference> sequencePreferences;

  /** Currently-selected Sprite Sequence. Set using
   * {@link #setSpriteSequence(String)}. */
  private String currentSequence = "";

  /** Current sprite to render */
  private Sprite currSprite;

  /** Time elapsed since the last sprite change in the sequence */
  private float timeSinceSpriteChange;

  private Color spriteColor;

  // region animation events

  /** Map of events with the key being the sprite order for which when the
   * index is 0 in the animation, we handle an event */
  private ObjectMap<String, AbstractEvent> eventsOnAnimationStart;

  /** Map of events with the key being the sprite order for which when the
   * index is {@link #spritePaths}'s size -1 in the animation, we handle an
   * event */
  private ObjectMap<String, AbstractEvent> eventsOnAnimationEnd;

  // endregion

  /** Current index in the animation */
  public int index = 0;

  // endregion

  // region constructors

  public SpriteSequenceDrawer(Renderer renderer, String name, boolean useRealDeltaTime)
  {

    super(renderer, name, useRealDeltaTime);
    eventsOnAnimationEnd = new ObjectMap<String, AbstractEvent>();
    eventsOnAnimationStart = new ObjectMap<String, AbstractEvent>();
    sequencePreferences = new ObjectMap<String, SpriteSequencePreference>();
    sequencePreferences.put("Default", new SpriteSequencePreference());

    this.currentSequence = null;
    this.spriteColor = new Color(1, 1, 1, 1);
    this.spritePaths = new ArrayList<String>();
    this.sequenceFilepaths = new ObjectMap<String, ArrayList<String>>();
  }

  // endregion constructors

  // region methods

  @Override
  public void draw(float delta)
  {

    if (currentSequence == null)
    {
      throw new RuntimeException(
          "You forgot to set the sprite order for the renderer.object "
              + renderer.object.getName() + " of type "
              + renderer.object.getType()
              + " with the sequence name " + name);
    }

    // region index in animation

    // increase time since current sprite by the time since last frame
    timeSinceSpriteChange += delta;

    // increase sprite index if the time since current sprite is more than
    // the expected time per sprite
    // achieved by dividing the overall animation loop time by the number of
    // sprites
    index += (int) (timeSinceSpriteChange / (getCurrPreferences()
        .getTimePerAnimation() / spritePaths.size()));

    // if the time since current sprite is bigger than expected, we use
    // modulus to bring it to a value lower
    // than the expected animation time per second.
    timeSinceSpriteChange = timeSinceSpriteChange
        % (getCurrPreferences().getTimePerAnimation() / spritePaths
            .size());

    // detects if we reached the end of the animation and the new one starts
    if (index % spritePaths.size() < index)
    {
      if (this.eventsOnAnimationStart.containsKey(currentSequence))
      {
        this.eventsOnAnimationStart.get(currentSequence).run();
      }
    }

    // value at following index will be the end of the animation, run end
    // event.
    if ((index + 1) % spritePaths.size() < index + 1)
    {
      if (this.eventsOnAnimationEnd.containsKey(currentSequence))
      {
        this.eventsOnAnimationEnd.get(currentSequence).run();
      }
    }

    index = index % spritePaths.size();
    // endregion

    Color tint = getCurrPreferences().getTintColor();
    float transparency = getCurrPreferences().getTransparency();
    // image is not visible due to a value of 0 on the color alpha channel,
    // so don't render.
    if (tint.a * transparency == 0)
      return;
    if (tint != null)
    {
      if (tint.r != spriteColor.r || tint.g != spriteColor.g
          || tint.b != spriteColor.b
          || tint.r * transparency != spriteColor.a)
      {
        spriteColor.set(tint.r, tint.g, tint.b, tint.a * transparency);
      }
    }

    // image is outside of the bounds of the camera wanting to draw it
    if (isOutOfBounds())
      return;

    // checks if the index changed and we have to change the image
    if (!currSprite.getTexture().equals(
      DolphinOES.assets.get(spritePaths.get(index),
            Texture.class)))
    {
      // get the Image from the Static sprite list
      currSprite.setTexture(DolphinOES.assets.get(
          spritePaths.get(index), Texture.class));
    }

    // setting tint color after setting the new texture.
    currSprite.setColor(spriteColor);

    // adjust position based on the renderer.object's position
    float targetPosX = renderer.object.pos.getX()
        + getCurrPreferences().getPositionOffset().x;
    float targetPosY = renderer.object.pos.getY()
        + getCurrPreferences().getPositionOffset().y;
    if (currSprite.getX() != targetPosX || currSprite.getY() != targetPosY)
    {
      currSprite.setPosition(targetPosX, targetPosY);
    }

    // setting the size if neccesary
    if (currSprite.getWidth() != getCurrPreferences().getSize().x
        || currSprite.getHeight() != getCurrPreferences().getSize().y)
      currSprite.setSize(getCurrPreferences().getSize().x,
          getCurrPreferences().getSize().y);

    // flips the images
    boolean flipX = getPreferences(currentSequence).isFlipX() != currSprite
        .isFlipX();
    boolean flipY = getPreferences(currentSequence).isFlipY() != currSprite
        .isFlipY();
    if (flipX || flipY)
    {
      currSprite.flip(flipX, flipY);
    }

    // draw image
    currSprite.draw(renderer.object.gameBatch.spriteBatch);

  }

  // region utility

  // region events on animation start/end
  public final void setEventOnAnimationStart(String sequenceName,
      AbstractEvent event)
  {

    eventsOnAnimationStart.put(sequenceName, event);
  }

  public final void removeEventOnAnimationStart(String sequenceName,
      AbstractEvent event)
  {

    if (eventsOnAnimationStart.containsKey(sequenceName))
    {
      eventsOnAnimationStart.remove(sequenceName);
    }
  }

  public final void setEventOnAnimationEnd(String sequenceName,
      AbstractEvent event)
  {

    eventsOnAnimationEnd.put(sequenceName, event);
  }

  public final void removeEventOnAnimationEnd(String sequenceName,
      AbstractEvent event)
  {

    if (eventsOnAnimationEnd.containsKey(sequenceName))
    {
      eventsOnAnimationEnd.remove(sequenceName);
    }
  }

  // endregion

  // region sprite sequences

  /** Sets the current sprite sequence to the value of passed parameter as a
   * key from {@link #sequenceFilepaths}.
   * <p>
   * If a Sequence with the passed parameter as a name is not found, an
   * exception will be returned.
   * <p>
   * If the sequence we want to change to is already the one that is set, the
   * change is not handled
   * 
   * @param sequenceName
   *            new Sprite Sequence name */
  public final void setSpriteSequence(String sequenceName)
  {

    if (!sequenceFilepaths.containsKey(sequenceName))
      throw new GdxRuntimeException("Sprite Sequence " + sequenceName
          + " for renderer of object " + renderer.object.getName()
          + " was not found in Sprite Sequence " + name);

    if (currentSequence == sequenceName
        && this.spritePaths.equals(this.sequenceFilepaths
            .get(sequenceName)))
      return;

    this.currentSequence = sequenceName;
    this.index = 0;
    this.spritePaths = this.sequenceFilepaths.get(sequenceName);
    this.currSprite = new Sprite(
        DolphinOES.assets.get(spritePaths.get(0),
            Texture.class));
    this.timeSinceSpriteChange = 0;

    if (sequencePreferences.containsKey("name"))
    {
      if (sequencePreferences.get(sequenceName).isPlaceAtEnd())
        renderer.placeAtEnd(this.name);

      if (sequencePreferences.get(sequenceName).isPlaceAtStart())
        renderer.placeAtStart(this.name);
    }
  }

  /** Adds a new sequence of sprite with the first parameter as the name of the
   * sequence. the file paths have to all contain the the root folder relative
   * to the project, file name and extensions
   * <p>
   * If no sprite sequence is set to run, the sequence we add will be the one
   * we will run.
   * 
   * @see {@link #addSpriteSequence(String, String, String[], String)} for a
   *      simpler way.
   * @param sequenceName
   *            name of the new sequence
   * @param files
   *            list of files in order with the path relative to the root
   *            folder */
  public final void addSpriteSequence(String sequenceName, String[] files)
  {

    this.sequenceFilepaths.put(sequenceName, new ArrayList<String>());

    for (int i = 0; i < files.length; i++)
    {
      this.sequenceFilepaths.get(sequenceName).add(files[i]);
    }

    if (this.currentSequence == null)
      setSpriteSequence(sequenceName);

  }

  /** Adds a new sequence of sprite with the first parameter as the name of the
   * sequence.
   * <p>
   * The filePath for a texture at index i will be folder+files[i]+extension.
   * <p>
   * The root folder of the files, file extensions and individual file name
   * relative to that folder are in individual parameters for the sake of
   * simplicity.
   * <p>
   * If no sprite sequence is set to run, the sequence we add will be the one
   * we will run.
   * 
   * @param sequenceName
   *            name of the new sequence
   * @param folder
   *            the root folder of the files
   * @param files
   *            list of files in order with the path relative to the root
   *            folder
   * @param extension
   *            file extensions, so as to not have to write .png for all and
   *            so on. */
  public final void addSpriteSequence(String sequenceName, String folder,
      String[] files, String extension)
  {

    this.sequenceFilepaths.put(sequenceName, new ArrayList<String>());
    this.sequenceFilepaths.get(sequenceName).clear();

    for (int i = 0; i < files.length; i++)
    {
      this.sequenceFilepaths.get(sequenceName).add(
          folder + files[i] + extension);
    }

    if (this.currentSequence == null)
      setSpriteSequence(sequenceName);

  }

  /** Adds a sprite setting to the {@link #sequencePreferences}. This will be
   * used when {@link #currentSequence } is equal to the first parameter. The
   * order or category is set using {@link #setSpriteSequence(String)}.
   * 
   * @param name
   *            The name of the sprite order to add a list of
   *            preferences/settings for
   * @param settings
   *            The renderer.object containing the settings data */
  public final void addPreferences(String name,
      SpriteSequencePreference settings)
  {

    sequencePreferences.put(name, settings);
  }

  /** Returns the {@link SpriteSequencePreference} from
   * {@link #sequencePreferences} at the passed parameter key.
   * 
   * @param name
   *            The name used when adding a sprite order setting in
   *            {@link #addPreferences(String, SpriteSequencePreference)}.
   * @return the value at the specific key. If that can't be found, returns
   *         the default settings. */
  public final SpriteSequencePreference getPreferences(String name)
  {

    if (sequencePreferences.containsKey(name))
    {
      return sequencePreferences.get(name);
    } else
    {
      return sequencePreferences.get("Default");
    }
  }

  /** Sets the default {@link SpriteSequencePreference}. Clears the current
   * preferences if clear is set to true in order to assure that those are the
   * only settings that are going to be used.
   * <p>
   * Afterwards, the settings for the default settings are updated to the new
   * value.
   * 
   * @param settings
   *            The new default sprite settings. If null, will not be used
   * @param whether
   *            to clear previous settings or not. */
  public final void setDefaultPreferences(SpriteSequencePreference settings,
      boolean clear)
  {

    if (settings == null)
      return;
    if (clear)
      sequencePreferences.clear();
    sequencePreferences.put("Default", settings);
  }

/** Returns the default sequence preferences. Those preferences run
     * only for sequences that we have not added a custom {@link SpriteSequencePreference} for
     * using {@link #addPreferences(String, SpriteSequencePreference).
     * 
     * @return default preferences.
     */
  public final SpriteSequencePreference getDefaultPreferences()
  {

    return sequencePreferences.get("Default");
  }

  private final SpriteSequencePreference getCurrPreferences()
  {

    if (sequencePreferences.containsKey(currentSequence))
    {
      return sequencePreferences.get(currentSequence);
    } else
    {
      return sequencePreferences.get("Default");
    }
  }

  // endregion

  @Override
  public final void dispose()
  {

    spriteColor = null;
    sequenceFilepaths.clear();
    sequencePreferences.clear();
    spritePaths.clear();
    currSprite = null;
  }

  // region out of bounds
  @Override
  protected final boolean isOutOfBounds()
  {
    float x = renderer.object.pos.getX() + getCurrPreferences().getPositionOffset().x;
    float y = renderer.object.pos.getY() + getCurrPreferences().getPositionOffset().y;
    Rectangle r = renderer.object.gameBatch.renderingArea;
    boolean overlaps =  
        x < r.x + r.width && 
        x + getCurrPreferences().getSize().x > r.x && 
        y < r.y + r.height && 
        y + getCurrPreferences().getSize().y > r.y;
    
    return !overlaps;
  }

  // endregion

  // endregion

  // endregion
}




Java Source Code List

com.sidereal.dolphinoes.architecture.AbstractEvent.java
com.sidereal.dolphinoes.architecture.DebugHandler.java
com.sidereal.dolphinoes.architecture.DolphinOES.java
com.sidereal.dolphinoes.architecture.GameBatch.java
com.sidereal.dolphinoes.architecture.GameBehavior.java
com.sidereal.dolphinoes.architecture.GameObject.java
com.sidereal.dolphinoes.architecture.GameScene.java
com.sidereal.dolphinoes.architecture.Module.java
com.sidereal.dolphinoes.architecture.core.Assets.java
com.sidereal.dolphinoes.architecture.core.Configurable.java
com.sidereal.dolphinoes.architecture.core.Debug.java
com.sidereal.dolphinoes.architecture.core.DolphinOESConfiguration.java
com.sidereal.dolphinoes.architecture.core.GameData.java
com.sidereal.dolphinoes.architecture.core.MouseMovedEvent.java
com.sidereal.dolphinoes.architecture.core.Time.java
com.sidereal.dolphinoes.architecture.core.assetload.AssetLoadHandler.java
com.sidereal.dolphinoes.architecture.core.assetload.AssetLoader.java
com.sidereal.dolphinoes.architecture.core.assetload.ClassFileHandleResolver.java
com.sidereal.dolphinoes.architecture.core.assetload.LoadingPercentage.java
com.sidereal.dolphinoes.architecture.core.input.ActionData.java
com.sidereal.dolphinoes.architecture.core.input.ActionEventWrapper.java
com.sidereal.dolphinoes.architecture.core.input.ActionEvent.java
com.sidereal.dolphinoes.architecture.core.input.Input.java
com.sidereal.dolphinoes.architecture.core.input.KeyTypedEvent.java
com.sidereal.dolphinoes.architecture.core.input.ScrollEvent.java
com.sidereal.dolphinoes.architecture.core.input.TouchData.java
com.sidereal.dolphinoes.architecture.core.input.TouchEventWrapper.java
com.sidereal.dolphinoes.architecture.core.input.TouchEvent.java
com.sidereal.dolphinoes.architecture.pos.Position.java
com.sidereal.dolphinoes.architecture.pos.Positions.java
com.sidereal.dolphinoes.backend.ConcreteGametester.java
com.sidereal.dolphinoes.backend.GameTester.java
com.sidereal.dolphinoes.backend.SceneGameTester.java
com.sidereal.dolphinoes.behaviors.audio.AudioListener.java
com.sidereal.dolphinoes.behaviors.audio.AudioPlayer.java
com.sidereal.dolphinoes.behaviors.events.EventTimer.java
com.sidereal.dolphinoes.behaviors.events.RecurringEvent.java
com.sidereal.dolphinoes.behaviors.input.Clickable.java
com.sidereal.dolphinoes.behaviors.particlesystem.ParticleEmitter.java
com.sidereal.dolphinoes.behaviors.particlesystem.ParticleHandler.java
com.sidereal.dolphinoes.behaviors.particlesystem.ParticleSpriteLayout.java
com.sidereal.dolphinoes.behaviors.particlesystem.ParticleSpriteObject.java
com.sidereal.dolphinoes.behaviors.pathfinding.PathfindingHandler.java
com.sidereal.dolphinoes.behaviors.pathfinding.PathfindingMap.java
com.sidereal.dolphinoes.behaviors.pathfinding.PathfindingNode.java
com.sidereal.dolphinoes.behaviors.pathfinding.PathfindingRoute.java
com.sidereal.dolphinoes.behaviors.renderer.DrawerBuilder.java
com.sidereal.dolphinoes.behaviors.renderer.Drawer.java
com.sidereal.dolphinoes.behaviors.renderer.Renderer.java
com.sidereal.dolphinoes.behaviors.renderer.ninepatch.NinepatchBuilder.java
com.sidereal.dolphinoes.behaviors.renderer.ninepatch.NinepatchDrawer.java
com.sidereal.dolphinoes.behaviors.renderer.scml.SCMLBuilder.java
com.sidereal.dolphinoes.behaviors.renderer.scml.SCMLDrawer.java
com.sidereal.dolphinoes.behaviors.renderer.sprite.SpriteBuilder.java
com.sidereal.dolphinoes.behaviors.renderer.sprite.SpriteDrawer.java
com.sidereal.dolphinoes.behaviors.renderer.spritesequence.SpriteSequenceBuilder.java
com.sidereal.dolphinoes.behaviors.renderer.spritesequence.SpriteSequenceDrawer.java
com.sidereal.dolphinoes.behaviors.renderer.spritesequence.SpriteSequencePreference.java
com.sidereal.dolphinoes.behaviors.renderer.texture.TextureBuilder.java
com.sidereal.dolphinoes.behaviors.renderer.texture.TextureDrawer.java
com.sidereal.dolphinoes.behaviors.renderer.tilemap.TileMapBuilder.java
com.sidereal.dolphinoes.behaviors.renderer.tilemap.TileMapDrawer.java
com.sidereal.dolphinoes.behaviors.triggers.Activatable.java
com.sidereal.dolphinoes.behaviors.triggers.Collider.java
com.sidereal.dolphinoes.behaviors.triggers.Hoverable.java
com.sidereal.dolphinoes.ui.MessageBubble.java
com.sidereal.dolphinoes.ui.TextBuilder.java
com.sidereal.dolphinoes.util.BooleanWrapper.java
com.sidereal.dolphinoes.util.DolphinOESException.java
com.sidereal.dolphinoes.util.FloatWrapper.java
com.sidereal.dolphinoes.util.IntWrapper.java
com.sidereal.dolphinoes.util.Utility.java