• Unity
  • No overload for 'OnSpineAnimation...' matches delegate...

Related Discussions
...

excuse me, what does this error mean?

No overload for 'OnSpineAnimationComplete' matches delegate 'AnimationState.TrackEntryDelegate' [Assembly-CSharp]
The type or namespace name 'TrackEntry' could not be found (are you missing a using directive or an assembly reference?) [Assembly-CSharp]

here is my script. (the variable boss is where I save spine animation references. and set animation is the thing I ask about yesterday which is now working.)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Spine.Unity;

public class PlayerNormalAttack : Skill
{
    int attackNumber = 0;
    public Skill nextAttack;
    public override void EnterAttack()
    {
        boss.set_animation(0,"attack1",false);
        attackNumber = 1;
    }
    void Awake()
    {
        boss.animationState.Complete += OnSpineAnimationComplete;
    }
    public void OnSpineAnimationComplete(TrackEntry trackEntry)
    {

}
}

the skill class

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Skill : MonoBehaviour
{
    // Start is called before the first frame update
    public Character boss;
    public int energyCost = 0;
    void Start()
    {
        boss = GetComponent<Character>();
    }

// Update is called once per frame
public virtual void EnterAttack(){}
public virtual void ProcessAttack(){}
public virtual void ExitAttack(){}
}

need some enlightment :wounded:

Just add a

using Spine;

statement at the top, it does not find Spine.TrackEntry.

Harald a écrit

Just add a

using Spine;

statement at the top, it does not find Spine.TrackEntry.

ah i see, thank you. It doesn't throw error now but...
Something weird happened.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Spine.Unity;
using Spine;

public class PlayerNormalAttack : Skill
{
    int attackNumber = 0;
    bool at_state = false;
    public Skill nextAttack;
    public override void EnterAttack()
    {
        Debug.Log("EXECUTE ATTACK");
        boss.set_animation(0,"attack1",false);
        attackNumber = 1;
        at_state = true;
    }
    void Awake()
    {
        boss.animationState.Complete += OnSpineAnimationComplete; // << THIS ONE
    }
    public void OnSpineAnimationComplete(TrackEntry trackEntry)
    {

}
}

When I comment out/delete boss.animationState.Complete += OnSpineAnimationComplete; it sets animation properly. But when I add/ uncomment it, it doesn't change animation and it keep repeating Debug.Log("EXECUTE ATTACK") over and over again.
hmm that doesn't make sense to me. I can't comprehend.

this is the Character class:
Somehow currentState keep showing States.Stand while nextState keep showing States.Attack. My guess is maybe it keep entering stand > attack > stand > attack but I don't know why. I never write any script that change state to States.Stand when in States.Attack. @.@
Or maybe because the script stopped executing... It didn't notify that it changed currentState.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Spine.Unity;

public class Character : MonoBehaviour
{
    // Animation
    public bool autoFacing = false;
    public Vector2 facing;
    public Vector2 targeting;
    public GameObject puppet;
    SkeletonAnimation skeletonAnimation;
    public Spine.AnimationState animationState;
    //Skeleton skeleton;

// Character Property
public float moveSpeed = 4f;
public Rigidbody2D rb;

// Skill slot
public Skill[] attackSlot = new Skill[1];

// Energy System
public bool unlimited_energy = false;
int energy;
int max_energy = 1000;
int energy_regeneration = 10;
int adrenaline_level = 10;
float regen_delay = 1f;

// State property (used for character movement)
float duration = 0f; // used in Evade state.
Vector2 freezed_movement; // used in Evade state.
int currentAttack = 0; // used in Attack state.

// Character Controller / Input
public Vector2 movement;
public bool dash = false;
public int attack = -1; // -1 = stop attacking. 0~.. index for attackSlot
public bool block = false;

//StateMachine
int previousState;
int currentState;
public int nextState;

// States
public enum States:int {
    Stand,
    Run,
    Evade,
    Sprint,
    Attack, //not implemented yet
    Block
};


void Start()
{
    rb = GetComponent<Rigidbody2D>();
    skeletonAnimation = puppet.GetComponent<SkeletonAnimation>();
    animationState = skeletonAnimation.AnimationState;
    //skeleton = skeletonAnimation.skeleton;
}

void FixedUpdate()
{
    // Energy system
    regen_delay -= Time.fixedDeltaTime;
    if (regen_delay <= 0f)
    {
        regen_delay += 0.1f;
        if (energy != max_energy)
        {
            energy = Mathf.Clamp(energy + energy_regeneration,0,max_energy);
            //Debug.Log("Energy : "+energy.ToString());
        }
    }
    // State Method
    if (currentState != nextState)
    {
        ExitState(currentState);
        previousState = currentState;
        EnterState(nextState);
        currentState = nextState;
        Debug.Log("Enter state: "+nextState.ToString());
    }
    ProcessState(currentState,Time.fixedDeltaTime);
}

bool useEnergy(int amount)
{
    if (hasEnoughEnergy(amount)){
        energy -= amount;
        return true;
    } else {
        return false;
    }
    
}
bool hasEnoughEnergy(int amount)
{
    if (energy >= amount){
        return true;
    }
    else {
        return false;
    }
}

void move(Vector2 amount) // to move character normally
{
    rb.MovePosition( rb.position + (amount*Time.fixedDeltaTime) );
}
void push(Vector2 amount) // to move character in one frame.
{
    
}

void refresh_facing() // for puppet
{
    transform.localScale = new Vector3(Mathf.Sign(facing.x),1,1);
}

void set_facing(float direction) // for puppet
{
    transform.localScale = new Vector3(Mathf.Sign(direction),1,1);
}

public void set_animation(int track,string animation, bool loop)
{
    if(skeletonAnimation.AnimationState.GetCurrent(track) == null || !string.Equals(skeletonAnimation.AnimationState.GetCurrent(track).Animation.Name,animation))
    {
        skeletonAnimation.AnimationState.SetAnimation(track,animation,loop);
    }
}

void EnterState(int state)
{
    switch (state)
    {
        case (int)States.Stand:
            set_animation(0,"stand",true);
            break;
        case (int)States.Run:
            set_animation(0,"run",true);
            break;
        case (int)States.Sprint:
            set_animation(0,"dash",true);
            break;

        case (int)States.Evade:
            set_facing(movement.x);
            //skeletonAnimation.AnimationState.TimeScale = 2f;
            set_animation(0,"roll",false);
            duration = 1f;
            freezed_movement = movement.normalized;
            break;
        case (int)States.Attack:
            attackSlot[currentAttack].EnterAttack();
            break;

        case (int)States.Block:
            set_animation(0,"block",false);
            break;

        default:
            break;
    }
}

void ProcessState(int state,float delta)
{
    switch (state)
    {
        
        case (int)States.Stand:
            if ( (movement.x != 0f) || (movement.y != 0f) )
            {
                nextState = (int)States.Run;
            }
            else if (block)
            {
                nextState = (int)States.Block;
            }
            else if (attack != -1)
            {
                nextState = (int)States.Attack;
            }

            if (autoFacing)
            {
                refresh_facing();
            }

            

            break;
       
        case (int)States.Run:
            //Debug.Log(skeletonAnimation.AnimationState.GetCurrent(0));
            move(movement.normalized*moveSpeed);
            if ( (movement.x == 0f) && (movement.y == 0f) )
            {
                nextState = (int)States.Stand;
            } 
            else if (dash)
            {
                nextState = (int)States.Evade;
            }
            else if (block)
            {
                nextState = (int)States.Block;
            }

            if (autoFacing)
            {
                if (Mathf.Sign(movement.x) == -Mathf.Sign(facing.x))
                {
                    set_animation(0,"run_back",true);
                }
                else if (Mathf.Sign(movement.x) == Mathf.Sign(facing.x))
                {
                    set_animation(0,"run",true);
                }
                refresh_facing();
            }

            break;
        
        case (int)States.Evade:
            duration -= Time.fixedDeltaTime;
            //Vector2 amount = freezed_movement[i]7f[/i]duration*moveSpeed;
            Vector2 amount = (freezed_movement[i]5f[/i]moveSpeed)[i]duration[/i]duration;
            move(amount);
            if (duration <= 0f)
            {
                nextState = (int)States.Run;
                if (dash)
                {
                    nextState = (int)States.Sprint;
                }
                else
                {
                    nextState = (int)States.Run;
                }
            }
            break;

        case (int)States.Sprint:
            move(movement.normalized[i]moveSpeed[/i]2f);
            if ( (movement.x == 0f) && (movement.y == 0f) )
            {
                nextState = (int)States.Stand;
            } 
            if (movement.x != 0)
            {
                set_facing(movement.x);
            }
            break;

        case (int)States.Attack:
            attackSlot[currentAttack].ProcessAttack();

            break;

        case (int)States.Block:
            if (!block)
            {
                nextState = (int)States.Stand;
            }
            break;

        default:
            break;
    }
}

void ExitState(int state)
{
    switch (state)
    {
        case (int)States.Evade:
            skeletonAnimation.AnimationState.TimeScale = 1f;
            break;
        
        case (int)States.Attack:
            attackSlot[currentAttack].ExitAttack();

            break;
        
        default:
            break;
    }
}
}

I will try to use other method for the time being... :confused:

EDIT
I also tried

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Spine.Unity;
using Spine;

public class PlayerNormalAttack : Skill
{
    int attackNumber = 0;
    bool at_state = false;
    public Skill nextAttack;
    public override void EnterAttack()
    {
        Debug.Log("EXECUTE ATTACK");
        boss.set_animation(0,"attack1",false);
        attackNumber = 1;
        at_state = true;
    }
    public override void ProcessAttack()
    {
        Debug.Log("processing");
    }
    // copas
    [SpineEvent] public string slashEventName = "slash"; 
    // copas
    void Awake()
    {
    // http://esotericsoftware.com/spine-unity-events Start
    var skeletonAnimation = GetComponent<SkeletonAnimation>();
    if (skeletonAnimation == null) return;
    skeletonAnimation.AnimationState.Event += HandleEvent;
    skeletonAnimation.AnimationState.Start += delegate (TrackEntry trackEntry) {
         Debug.Log(string.Format("track {0} started a new animation.", trackEntry.TrackIndex));
      };

  skeletonAnimation.AnimationState.End += delegate {
     // ... or choose to ignore its parameters.
     Debug.Log("An animation ended!");
  };
// http://esotericsoftware.com/spine-unity-events Start
}
void HandleEvent(TrackEntry trackEntry, Spine.Event e)
{
    Debug.Log("SPINE EVENT");
  if (e.Data.Name == slashEventName) {
     Debug.Log("Play a footstep sound!");
    }
}
}

but this does not call either Debug.Log("An animation ended!"), Debug.Log("Play a footstep sound!"), and Debug.Log(string.Format("track {0} started a new animation.", trackEntry.TrackIndex)) at all. 😢

If I used Start() instead of Awake() like in the guide on that page. The same with the earlier problem happened, the one that had multiple "EXECUTE ATTACK" messages.

As you posted your callback method OnSpineAnimationComplete() as empty, either there is something missing or it is not the cause of your problem.

Having three states instead of two int previousState, int currentState, public int nextState also seems fishy at a quick look. I would advise to use only two, int previousState, int currentState or whatever you might call them. You can also have a look at the Spine Examples/Getting Started/5 Basic Platformer example scene and the BasicPlatformerController.cs script and related scripts used therein.

Also be sure to understand when each event is fired in regards to looping and the like:
AnimationStateListener Methods