Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sample project available ? #8

Open
aallnneess opened this issue Nov 13, 2021 · 3 comments
Open

Sample project available ? #8

aallnneess opened this issue Nov 13, 2021 · 3 comments

Comments

@aallnneess
Copy link

Hey :-)

Very cool Project!

Is there a sample project somewhere to better understand how this library works?

Thanks

@WinterAlexander
Copy link
Owner

WinterAlexander commented Nov 15, 2021

Hi,

A sample project would be a great idea but unfortunately there's none available right now (I only use it in MakerKing which is currently closed source because of its central server). If anyone has the time to create one though, I'll make sure to link it! I don't have the time to make a full blown example project but here's a quick summary on how to use this project with Spriter 1 though:

Load your .scml file into the game. If you use libGDX's assetManager you can do

setLoader(SCMLProject.class, ".scml", new SCMLLoader(getFileHandleResolver()));

and then load your project like

load("your_project.scml", SCMLProject.class);

(otherwise load it manually using the SCMLReader class).

Once you have your SCMLProject loaded, you can query Entity from SCMLProject using SCMLProject#getEntity(). From this Entity, load the animation you want to draw using Entity#getAnimation(String name) and call update(float) to update your animation (on tick) and draw(Batch) to draw it on screen. Change the position and scale of the root object to move the animation where you want to draw it.

Personally I wrapped all of the management of the Animation class into a renderer in my project:


/**
 * Uses gdx-animation to render animations
 * <p>
 * Created on 2018-01-22.
 *
 * @author Alexander Winter
 */
public class GdxAnimationRenderer<T extends Localizable>
		extends ConstPriorityRenderer
		implements AnimationRenderer
{
	protected AssetSupplier assets;

	protected final T object;
	private final String spriterEntityName;
	private final float scale;

	/**
	 * When set to true, the animation will keep looping even if its not a looping animation
	 */
	protected boolean forceLooping = false;

	protected final Vector2 animationOffset;

	private Entity entity;
	private boolean enabled = true, currentAnimationChanged = false;

	private float initialTime = -1f;

	private final Queue<AnimationIdentifier> animations = new Queue<>();
	protected Animation current = null;

	public GdxAnimationRenderer(T object,
	                            RenderPriority<?> priority,
	                            String spriterEntityName,
	                            float scale,
	                            int initialAnimation)
	{
		this(object, priority, spriterEntityName, scale, initialAnimation, new Vector2());
	}

	public GdxAnimationRenderer(T object,
	                            RenderPriority<?> priority,
	                            String spriterEntityName,
	                            float scale,
	                            String initialAnimation)
	{
		this(object, priority, spriterEntityName, scale, initialAnimation, new Vector2());
	}

	public GdxAnimationRenderer(T object,
	                            RenderPriority<?> priority,
	                            String spriterEntityName,
	                            float scale,
	                            int initialAnimation,
	                            Vector2 animationOffset)
	{
		super(priority);
		ensureNotNull(object, "object");
		ensureNotNull(priority, "priority");
		ensureNotNull(spriterEntityName, "spriterEntityName");
		ensureNotNull(animationOffset, "animationOffset");

		this.object = object;
		this.spriterEntityName = spriterEntityName;
		this.scale = scale;
		queueAnimation(initialAnimation);
		this.animationOffset = animationOffset;
	}

	public GdxAnimationRenderer(T object,
	                            RenderPriority<?> priority,
	                            String spriterEntityName,
	                            float scale,
	                            String initialAnimation,
	                            Vector2 animationOffset)
	{
		super(priority);

		ensureNotNull(object, "object");
		ensureNotNull(priority, "priority");
		ensureNotNull(spriterEntityName, "spriterEntityName");
		ensureNotNull(initialAnimation, "initialAnimation");
		ensureNotNull(animationOffset, "animationOffset");

		this.object = object;
		this.spriterEntityName = spriterEntityName;
		this.scale = scale;queueAnimation(initialAnimation);
		this.animationOffset = animationOffset;
	}

	@Override
	public void init(RenderScreen screen)
	{
		this.assets = screen.getAssets();
		resetEntity();
	}

	@Override
	public void render(RenderScreen screen)
	{
		if(!isAnimationPlaying())
			return;

		update(screen.getDeltaTime());

		if(enabled && current != null)
			current.draw(screen.getBatch());
	}

	protected void update(float deltaTime)
	{
		if(!isAnimationPlaying())
			return;

		Animation firstInQueue = animations.first().getAnimation(entity);

		if(currentAnimationChanged || current != firstInQueue)
		{
			current = firstInQueue;
			current.reset();

			if(initialTime != -1f)
			{
				current.setTime(initialTime * 1000f);
				initialTime = -1f;
			}
			currentAnimationChanged = false;
		}

		if(current.isDone())
		{
			if(forceLooping)
				current.reset();
			else
			{
				animations.removeFirst();

				if(animations.size == 0)
				{
					current = null;
					return;
				}

				current = animations.first().getAnimation(entity);
				current.reset();
			}
		}

		setRootProperties();
		current.update(1000f * deltaTime);
	}

	protected void setRootProperties()
	{
		current.getRoot().getPosition().set(object.getPosition()).add(animationOffset);
		if(object instanceof Flipped)
		{
			current.getRoot().setFlippedX(((Flipped)object).isFlippedX());
			current.getRoot().setFlippedY(((Flipped)object).isFlippedY());
		}

		current.getRoot().setAngle(getVisualAngle());
	}

	protected float getVisualAngle()
	{
		if(object instanceof Rotated)
			return ((Rotated)object).getAngle();
		return 0f;
	}

	@Override
	public void setCurrentAnimation(String name)
	{
		animations.clear();
		currentAnimationChanged = true;
		queueAnimation(name);
	}

	@Override
	public void setCurrentAnimation(int id)
	{
		animations.clear();
		currentAnimationChanged = true;
		queueAnimation(id);
	}

	@Override
	public int getCurrentAnimationId()
	{
		if(animations.size == 0)
			throw new IllegalStateException("No current animation");

		AnimationIdentifier current = animations.first();

		if(current instanceof IndexIdentifier)
			return ((IndexIdentifier)current).getIndex();

		if(getEntity() == null)
			throw new IllegalStateException("Renderer not initialized");

		return getEntity().getAnimationId(current.getAnimation(getEntity()));
	}

	@Override
	public String getCurrentAnimationName()
	{
		if(animations.size == 0)
			throw new IllegalStateException("No current animation");

		AnimationIdentifier current = animations.first();

		if(current instanceof NameIdentifier)
			return ((NameIdentifier)current).getName();

		if(getEntity() == null)
			throw new IllegalStateException("Renderer not initialized");

		return animations.first().getAnimation(getEntity()).getName();
	}

	@Override
	public float getAnimationTime()
	{
		if(current == null || currentAnimationChanged)
			return initialTime == -1f ? 0f : initialTime;

		return current.getTime() / 1000f;
	}

	@Override
	public float getAnimationLength()
	{
		return current.getLength() / 1000f;
	}

	@Override
	public float getAnimationSpeed()
	{
		return current.getSpeed();
	}

	@Override
	public void setAnimationTime(float time)
	{
		if(current != null && !currentAnimationChanged)
			current.setTime(time * 1000f);
		else
			initialTime = time;
	}

	public boolean isDone()
	{
		return current != null && current.isDone();
	}

	public void resetEntity()
	{
		entity = assets.getSpriterProject().getEntity(spriterEntityName);

		if(entity == null)
			throw new RuntimeException("Spriter entity not found");

		entity.getAnimations().forEach(anim -> anim.getRoot().setScale(scale));
	}

	@Override
	public boolean isAnimationLooping()
	{
		return forceLooping || current != null && current.isLooping();
	}

	@Override
	public void queueAnimation(int animationId)
	{
		animations.addLast(new IndexIdentifier(animationId));
	}

	@Override
	public void queueAnimation(String animationName)
	{
		animations.addLast(new NameIdentifier(animationName));
	}

	@Override
	public void clearAnimationQueue()
	{
		while(animations.size > 1)
			animations.removeLast();
	}

	@Override
	public void skipAnimation()
	{
		animations.removeFirst();
		currentAnimationChanged = true;
	}

	protected boolean isAnimationAboutToChange()
	{
		return currentAnimationChanged || current != animations.first().getAnimation(entity);
	}

	@Override
	public boolean isAnimationPlaying()
	{
		return animations.size != 0;
	}

	@Override
	public int getQueueSize()
	{
		return max(0, animations.size - 1);
	}

	public Entity getEntity()
	{
		return entity;
	}

	public boolean isEnabled()
	{
		return enabled;
	}

	public void setEnabled(boolean enabled)
	{
		this.enabled = enabled;
	}

	@Override
	public boolean isReady()
	{
		return getEntity() != null;
	}

	public Vector2 getAnimationOffset()
	{
		return animationOffset;
	}
}

This lets me easily change the animation for a specific entity and etc.

Hope this helps slightly and I apologize for the lack of support/documentation. I don't think anyone ever used this project beside me.

Best,
Winter

@aallnneess
Copy link
Author

Hello,

thanks for your answer.

Yes, it is surprising that there is no well-documented runtime for the free spriter. I think there was already a lot of potential. However, it is too late for that, since Spriter 1 itself will no longer exist. Or further updated. And Spriter 2 ... well: D

I've been thinking back and forth whether I should invest any more time there. But then I decided on Spine.

So sorry that I wasted your time. At least a little guide has been created here for people who may be interested.

Thanks and good times.
matze

@WinterAlexander
Copy link
Owner

Yes, it is surprising that there is no well-documented runtime for the free spriter.

This repo is quite well documented and works with the free spriter: https://github.com/Trixt0r/spriter

The only thing is that it is not made for libGDX but for any Java project. This project gdx-animation is basically a fork of the project I linked but made for libGDX, usage is nearly identical.

If you have the money I indeed recommend you to get Spine instead. I personally only use Spriter 1 because it is free. Spine has mesh transformation technology which allow for much better animations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants