Hello Guest

Author Topic: Frustration with tk2d  (Read 8733 times)

delogan

  • Guest
Frustration with tk2d
« on: June 15, 2013, 04:33:43 pm »
Hi!
Recently I have had troubles with certain features lacking in current version of tk2d (2.0) such as reverse play, flexible fps offsets, animation updates etc.
Discussions about adding some of those features have been seen over a year ago http://unikronsoftware.com/2dtoolkit/forum/index.php?topic=287.0, and they are yet to be implemented. For example, currently the only way to reverse-play your animation is through manually adding the reverse version of the animation in unity editor, and then playing that normally using Play(). Realistically, this is not practical since any complete game containts tens, if not hundreds of animated sprite sequences, and jumping through hoops to play it in reverse is not something anyone looks forward to.
Sure, you can play animation manually, but it turned out to be a nightmare with plethora of null reference errors along the way, since the animation is paused during manual play to control the timeline.
Even changing a frame during runtime is impossible due to null reference to .CurrentFrame (remember, the animation is paused).

This is very heartbreaking. I mean, pixelperfect rendering looks immaculate, and all of that with just two(!!) draw calls.

So I plead to you, almighty Unikron, PLEASE add some straightforward features that are actually usable. Not collider generators, not fancy WrapModes or static sprite batchers.
Just some simple tools to actually animate the sprites.

Thanks.

unikronsoftware

  • Administrator
  • Hero Member
  • *****
  • Posts: 9709
    • View Profile
Re: Frustration with tk2d
« Reply #1 on: June 15, 2013, 06:26:54 pm »
1. The most efficient way of playing a clip in reverse is to create an explicit reversed clip.
Based on all the research into the options at the time, we came up with the animation editor operators feature (http://unikronsoftware.com/2dtoolkit/doc/2.00/advanced/animation_editor_operators.html), which effectively lets you add features directly into the animation editor. You can basically make it so one click will create reversed animations for all your animations in the current animation collection. Yes, you still need to code the operator to do it, but refer to tk2dSpriteAnimatorOp.cs - you can add as many operators as you like, and its suprisingly easy to do so. In particular, refer to the CopyAnimation one, as a you could create reversed clips for all your clips.

The second thing we did was to transition from a play from library concept to a "play a clip". This means you can, at runtime, build up your tk2dSpriteAnimationClip and simply feed that to the animator to play. You can reverse your clip (trivial, if you don't need fancy wrap modes) and feed the wrapped one at runtime should you need to.

2. What are flexible fps offsets, animation updates?

The thing is there has been very few requests for these features :) As you found, there was 1 request a year ago, and thats about it mostly. We decide on features based on requests (and our needs for our own games / use). I guess not many people need to play animations in reverse, or those that do find it easy enough to simply work around it?

We've had the need to control stepping the animation manually, i.e. reduce and increase fps based on what the player is doing, etc, but almost never required playing a clip in reverse. If you would be as kind as to explain your use case, that will most certainly help, either in putting the feature into the wishlist.

Also, why do you pause the animation to control the timeline? You can simply disable the tk2dSpriteAnimator component (enabled = false), and then call UpdateAnimation explicitly from your own code with your own deltaTime. It doesn't cope with negative delta times, but movign forward should work fine.

delogan

  • Guest
Re: Frustration with tk2d
« Reply #2 on: June 15, 2013, 06:54:08 pm »
Thanks for a prompt reply.

Look, all I am trying to say, is that tk2d is the go-to unity package when creating a 2/2.5D games. Seeing it advance so far amazes me to no end, but at the same time one can wonder why are there so many straightforward basic features missing. I sincerely believe that one should not be writing operators for every type of animation when it all could be easily avoided with a simple function.

I did not realize however, that other methods were inefficient, and that you have to use basic built-ins not to sacrifice performance. I apologize for misunderstanding.


unikronsoftware

  • Administrator
  • Hero Member
  • *****
  • Posts: 9709
    • View Profile
Re: Frustration with tk2d
« Reply #3 on: June 15, 2013, 07:39:59 pm »
If you only need this for looped and/or single play clips, drop this extension class somewhere in your project, and then after getting a clip, call .Reverse() to get a reversed clip. The reversed clip is cached, so you only pay the cost of the Reverse()

Code: [Select]
public static class tk2dSpriteAnimationClipExtensions {
static Dictionary<tk2dSpriteAnimationClip, tk2dSpriteAnimationClip> reverseTable = new Dictionary<tk2dSpriteAnimationClip, tk2dSpriteAnimationClip>();

public static tk2dSpriteAnimationClip Reverse(this tk2dSpriteAnimationClip clip) {
tk2dSpriteAnimationClip reversed = null;
if (!reverseTable.TryGetValue(clip, out reversed)) {
reversed = new tk2dSpriteAnimationClip();
reversed.CopyFrom(clip);
System.Array.Reverse(reversed.frames);
reverseTable.Add(clip, reversed);
reverseTable.Add(reversed, clip);
}
return reversed;
}
}

Eg.
Code: [Select]
animator.Play( animator.Library.GetClipByName("someclip").Reverse() );


You still haven't really told me what your use case for these reversed clips..., or answered #2. I really do need to know if I were to even consider adding them as a feature.
Edit: Adding something that works for one particular case (eg. only for simple looped / one shot animations) is trivial. Making it work and not break existing data, AND work with all possible cases on the other hand, isn't quite as easy.
« Last Edit: June 15, 2013, 08:15:29 pm by unikronsoftware »

foggery

  • Newbie
  • *
  • Posts: 2
    • View Profile
Re: Frustration with tk2d
« Reply #4 on: May 07, 2014, 11:49:50 am »
Hello, i know this post is old, but i have some issues with this.


I tried the extension and it works so far. But not realiy in the way i want it.

What i need is a seamless forward and backward playing.

For Example:

(Left Click -> Start Animation)  1-2-3-4-5-6-7-8-9-10 (Right Click -> Stop Animation at current frame/time -> Play Reverse/Backwards) 9-8-7-6-5 (Left Click -> Stop Animation at current frame/time -> Play Forward) 6-7-8-9-10 ...

What is it good for:

You can use it for roll-over/indicator Buttons/Animation Effects. Or for more complex interface/ui designs.

I'm not really a programmer so i hope someone can help me getting the code together. This is the code with the extension included so far. Problem is that the animation makes a jump and is not seamless. Maybe there is another easy way without the extension.

Code: [Select]
using UnityEngine;
using System.Collections;

public class Mouse_Click : MonoBehaviour {

public tk2dSpriteAnimator animator;


void Start () {
animator = GetComponent<tk2dSpriteAnimator>();
}


void Update () {

if (Input.GetMouseButtonDown(0))
{
animator.Play( animator.Library.GetClipByName("Test_Interface_Animation") );
}
if (Input.GetMouseButtonDown(1))
{
animator.Play( animator.Library.GetClipByName("Test_Interface_Animation").Reverse() );
}

}
}


Thank you!


niall111

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: Frustration with tk2d
« Reply #5 on: May 07, 2014, 01:38:59 pm »
Don't you want to use PlayFromFrame then?

your line:
Code: [Select]
animator.Play( animator.Library.GetClipByName("Test_Interface_Animation").Reverse() );

could be:

Code: [Select]
animator.PlayFromFrame( animator.Library.GetClipByName("Test_Interface_Animation").Reverse(), animator.CurrentFrame );

Should do it?

foggery

  • Newbie
  • *
  • Posts: 2
    • View Profile
Re: Frustration with tk2d
« Reply #6 on: May 07, 2014, 03:30:47 pm »
Ok some simple math did the trick.

Here is my working code for now.
I dont now if this is a clean/usual solution.
If anyone has better ideas, please povide me
with this. Btw. next step would be to write
an PlayMaker Action for this.

If someone is interested ;)?

Code: [Select]
using UnityEngine;
using System.Collections;

public class Mouse_Click : MonoBehaviour {

private tk2dSpriteAnimator animator;
private bool isAnimating = false; // Check if animation is playing, only allow revers when animation is playing
public int totalFrames; // Number of total frames in animation to match seamless reverse


// Use this for initialization
void Start () {
animator = GetComponent<tk2dSpriteAnimator>();
}

// Update is called once per frame
void Update () {

if (Input.GetMouseButtonDown(0))
{
animator.Play( animator.Library.GetClipByName("Test_Interface_Animation") );
isAnimating = true;
}
if (Input.GetMouseButtonDown(1))
{
if (isAnimating) {
animator.PlayFromFrame( animator.Library.GetClipByName("Test_Interface_Animation").Reverse(), totalFrames - animator.CurrentFrame);
isAnimating = false;
}

}

}
}

twicecircled

  • 2D Toolkit
  • Newbie
  • *
  • Posts: 4
    • View Profile
Re: Frustration with tk2d
« Reply #7 on: May 22, 2014, 12:35:50 pm »
I thought I would share the code I'm using which lets you reverse an animation by simply switching a boolean on the tk2dSpriteAnimator itself. This involves changing the core code itself so obviously you will lose any modifications you make if you update/reinstall TK2D.

The nice thing about this code is you can reverse in the middle of the animation and it keeps its position.

Here's the changes I've made to the code:

TK2DROOT\tk2d\Code\Sprites\tk2dSpriteAnimator.cs
Line 47 - added a publically accessible bool which you can toggle to reverse the animation.
Code: [Select]
public bool reversed = false;
Line 546(ish) - Replace UpdateAnimation(float deltaTime) method - when 'reversed' is true, clipTime is decremented instead of incremented. I've also made a few changes to how it calculates the Frame number so that is supports negative clip time. Note: Reverse mode is only supported when on wrap mode "loop" as that is all I needed it for.
Code: [Select]
/// <summary>
/// Modified by Twice Circled to add reverse behaviour for looping anims - 22/05/14
/// Steps the animation based on the given deltaTime
/// Disable tk2dSpriteAnimator, and then call UpdateAnimation manually to feed your own time
/// eg. when you need an animation to play when the game is paused using Time.timeScale.
/// </summary>
public void UpdateAnimation (float deltaTime) {
// Only process when clip is playing
var localState = state | globalState;
if (localState != State.Playing) {
return;
}

// Current clip should not be null at this point
// Only supports reverse behaviour on 'loop'
if (reversed && currentClip.wrapMode == tk2dSpriteAnimationClip.WrapMode.Loop) {
clipTime -= deltaTime * clipFps;
} else {
clipTime += deltaTime * clipFps;
}
int _previousFrame = previousFrame;

switch (currentClip.wrapMode) {
case tk2dSpriteAnimationClip.WrapMode.Loop:
case tk2dSpriteAnimationClip.WrapMode.RandomLoop:
{
int currFrame = ((int)clipTime % currentClip.frames.Length + currentClip.frames.Length) % currentClip.frames.Length;
SetFrameInternal (currFrame);
// Only supports reverse behaviour on 'loop'
if (reversed && currentClip.wrapMode == tk2dSpriteAnimationClip.WrapMode.Loop) {
if (currFrame > _previousFrame) { // wrap around
ProcessEvents (_previousFrame, 0, -1); // back to beg of clip
ProcessEvents (currentClip.frames.Length, currFrame, -1); // process back to current frame
} else {
ProcessEvents (_previousFrame, currFrame, -1);
}
} else {
if (currFrame < _previousFrame) { // wrap around
ProcessEvents (_previousFrame, currentClip.frames.Length - 1, 1); // up to end of clip
ProcessEvents (-1, currFrame, 1); // process up to current frame
} else {
ProcessEvents (_previousFrame, currFrame, 1);
}
}
break;
}

case tk2dSpriteAnimationClip.WrapMode.LoopSection:
{
int currFrame = (int)clipTime;
int currFrameLooped = currentClip.loopStart + ((currFrame - currentClip.loopStart) % (currentClip.frames.Length - currentClip.loopStart));
if (currFrame >= currentClip.loopStart) {
SetFrameInternal (currFrameLooped);
currFrame = currFrameLooped;
if (_previousFrame < currentClip.loopStart) {
ProcessEvents (_previousFrame, currentClip.loopStart - 1, 1); // processed up to loop-start
ProcessEvents (currentClip.loopStart - 1, currFrame, 1); // to current frame, doesn't cope if already looped once
} else {
if (currFrame < _previousFrame) {
ProcessEvents (_previousFrame, currentClip.frames.Length - 1, 1); // up to end of clip
ProcessEvents (currentClip.loopStart - 1, currFrame, 1); // up to current frame
} else {
ProcessEvents (_previousFrame, currFrame, 1); // this doesn't cope with multi loops within one frame
}
}
} else {
SetFrameInternal (currFrame);
ProcessEvents (_previousFrame, currFrame, 1);
}
break;
}

case tk2dSpriteAnimationClip.WrapMode.PingPong:
{
int currFrame = (currentClip.frames.Length > 1) ? ((int)clipTime % (currentClip.frames.Length + currentClip.frames.Length - 2)) : 0;
int dir = 1;
if (currFrame >= currentClip.frames.Length) {
currFrame = 2 * currentClip.frames.Length - 2 - currFrame;
dir = -1;
}
// This is likely to be buggy - this needs to be rewritten storing prevClipTime and comparing that rather than previousFrame
// as its impossible to detect direction with this, when running at frame speeds where a transition occurs within a frame
if (currFrame < _previousFrame) {
dir = -1;
}
SetFrameInternal (currFrame);
ProcessEvents (_previousFrame, currFrame, dir);
break;
}

case tk2dSpriteAnimationClip.WrapMode.Once:
{
int currFrame = (int)clipTime;
if (currFrame >= currentClip.frames.Length) {
SetFrameInternal (currentClip.frames.Length - 1); // set to last frame
state &= ~State.Playing; // stop playing before calling event - the event could start a new animation playing here
ProcessEvents (_previousFrame, currentClip.frames.Length - 1, 1);
OnAnimationCompleted ();
} else {
SetFrameInternal (currFrame);
ProcessEvents (_previousFrame, currFrame, 1);
}
break;
}
}
}

Usage: Once set up the usage is very simple. The following code reverses the animation when you press the middle mouse button.
Code: [Select]

// Script attached to GameObject with a tk2dSpriteAnimator component attached.
void Update () {
tk2dSpriteAnimator animator = GetComponent<tk2dSpriteAnimator>();
if (Input.GetMouseButtonDown (2)) {
animator.reversed = !animator.reversed;
}
}

Just copy and paste the code above into your tk2dSpriteAnimator.cs script. DISCLAIMER: Use at your own risk, there is no guarantee that this code will continue to work in future versions of tk2d.
« Last Edit: May 22, 2014, 12:39:13 pm by twicecircled »