Simple Animation Script Issues

Hello everyone,

I am trying to write a simple animation script to control a hockey character asset that I found on the Unity asset store. After having some issues with using an Animator Controller, I decided to stick with the regular Animation component and I have had some success. The character performs the animations accordingly, but I am having some issues switching between them and playing them accordingly. The animations include idle, shoot, and celebrate. A script attached to the character controls whether they should be played. The script is as follows:

using UnityEngine;
using System.Collections;

public class HockeyPlayerController: MonoBehaviour {

    Animation hpAnim;
    bool shooting, celebrating;

    void Start ()
    {
        hpAnim = GetComponent<Animation>();
        hpAnim.wrapMode = WrapMode.Once;
    }

    void Update()
    {
        // Simple Animation Control
        if (!shooting && !celebrating)
        {
            hpAnim.Play("Idle");
        }
        else if (shooting && !celebrating)
        {
            hpAnim.Play("SlowSkatePoseShoot");
            shooting = false;
        }
        else if (celebrating && !shooting)
        {
            hpAnim.Play("Score");
            celebrating = false;
        }
    }

    public void hpShoot()
    {
        shooting = true;
    }

    public void hpCelebrate()
    {
        celebrating = true;
    }
}

I am going to be calling hpShoot and hpCelebrate from other scripts.

A few issues I am having include that the animations do not play once, but loop, regardless of Wrap Mode I set them at. For idle that is fine, but for shooting and celebrating you can probably see why that is an issue. For testing, I call the hpShoot function in another script and it loops. If the player scores, it should call the hpCelebrate function, but the player goes still. Do you see anything wrong here that I am missing? Thanks for all of your help in advance.

EDIT:

Further testing these animations, I assigned them to keyboard buttons to play them. Sure enough, they all play accordingly. Here is what I did:

    void Update()
    {
        // Simple Animation Control
        if (Input.GetKeyUp(KeyCode.A))
        {
            hpAnim.Play("Idle");
        }
        else if (Input.GetKeyUp(KeyCode.S))
        {
            hpAnim.Play("SlowSkatePoseShoot");
            shooting = false;
        }
        else if (Input.GetKeyUp(KeyCode.D))
        {
            hpAnim.Play("Score");
            celebrating = false;
        }
    }

How can I get this to work otherwise? Basically what should happen is the player shoots or celebrates then goes back into idle state.

I’m really at a loss here. Anyone have any idea what I could do?

EDIT: I’ve been working on changing the script, but the animations still keep looping!

public class HockeyPlayerController: MonoBehaviour {

    Animation hpAnim;
    bool shooting, celebrating;
    private HPState state;

    private enum HPState
    {
        Idle,
        Shooting,
        Celebrating
    }

    void Start ()
    {
        hpAnim = GetComponent<Animation>();
        hpAnim["Idle"].wrapMode = WrapMode.Loop;
        hpAnim["SlowSkatePoseShoot"].wrapMode = WrapMode.Once;
        hpAnim["Score"].wrapMode = WrapMode.Once;
     
    }

    void Update()
    {
        switch (state)
        {
            case HPState.Idle:

                hpAnim.Play("Idle");

                if (shooting)
                {
                    state = HPState.Shooting;
                }
             
                if (celebrating)
                {
                    state = HPState.Celebrating;
                }
                break;

            case HPState.Shooting:

                hpAnim.Play("SlowSkatePoseShoot");
                shooting = false;
                break;

            case HPState.Celebrating:

                hpAnim.Play("Score");
                celebrating = false;
                break;
        }
    }

    public void hpShoot()
    {
        shooting = true;
    }

    public void hpCelebrate()
    {
        celebrating = true;
    }
}

In regards to the animations looping, check the imported animation file and make sure that “Loop Time” is not checked.

Are you referring to the file in the Assets folder? I checked this on all of them and the only option I can change is Wrap Mode, which I have changed to Once or Loop accordingly.

Sorry about that…I was working with a humanoid import when I read your question. I am referring to the file in your Assets. If you set the Wrap mode to “Once” it should not loop automatically. I’m trying to see where your code changes from HPState.Shooting or HPState.Celebrating back to HPState.Idle. There appears to be nowhere that the state transitions and therefore you get stuck in the current state. Give me a bit and I’ll get some code going on this one.

1 Like

Thank you, TrickyHandz, I’ve been stuck at this part in my game for days!

Give this a try…I added some comments to help explain my thinking on it.

using UnityEngine;

public class HockeyPlayerController : MonoBehaviour
{
    private enum HPState
    {
        Idle,
        Shooting,
        Celebrating,
    }

    private HPState state = HPState.Idle;
    private Animation hpAnim;

    void Start()
    {
        hpAnim = this.GetComponent<Animation>();
        state = HPState.Idle;
    }


    void Update()
    {
        switch (state) {
            case HPState.Idle:
                // Check to see that other animations
                // are not playing before going to Idle
                if (!hpAnim.IsPlaying("SlowSkatePoseShoot") && !hpAnim.IsPlaying("Score")) {
                    hpAnim.Play("Idle");
                }
                break;

            case HPState.Shooting:
                hpAnim.Play("SlowSkatePoseShoot");
                // Set the state to Idle to
                // allow for transition
                state = HPState.Idle;
                break;

            case HPState.Celebrating:
                hpAnim.Play("Score");
                state = HPState.Idle;
                // Set the state to Idle to
                // allow for transition
                break;
        }
    }

    // There is no need to set a bool in
    // these methods, so we just set the
    // appropriate state instead.
    public void hpShoot()
    {
        state = HPState.Shooting;
    }

    public void hpCelebrate()
    {
        state = HPState.Celebrating;
    }
}

I haven’t really messed around with this way of handling animation since before 4.0 so I can’t even remember if this is the best way to do things, but I think it should get the intended result. Also, I don’t think there is any reason you couldn’t handle this with a Generic Rig and a Mecanim State Machine. It may be less code if you can go the Generic Rig route.

Thanks! The problem is the asset that I am using uses legacy animations, and there is a separate part that the character is using (hockey stick). When I tried to rig the model, it seems to break. An avatar was not included unfortunately. The only way I have gotten these to work is using the regular animation component. I am seeing that my issue lies more in the way I am coding and calling these animations to play. I am calling these animations in the Game Controller as follows (I’m just testing one player for now):

    void Start ()
    {
        // Find the hockey players
        //hp1 = GameObject.FindGameObjectWithTag("HockeyPlayer1");
        hp2 = GameObject.FindGameObjectWithTag("HockeyPlayer2");
        //hp3 = GameObject.FindGameObjectWithTag("HockeyPlayer3");

        // Get hockey players' controller scripts
        //hp1Control = hp1.GetComponent("HockeyPlayerController") as HockeyPlayerController;
        hp2Control = hp2.GetComponent("HockeyPlayerController") as HockeyPlayerController;
        //hp3Control = hp3.GetComponent("HockeyPlayerController") as HockeyPlayerController;

        timer = 10.0f;
        HockeyPlayerSelection();
    }

    void Update()
    {
        timer -=Time.deltaTime;
        Debug.Log(timer);

        if (timer <= 0f)
        {
            HockeyPlayerSelection();
            timer = 10.0f;
        }
    }

    // Select a hockey player to shoot
    void HockeyPlayerSelection()
    {
        System.Random rand = new System.Random();
        int num = rand.Next(2,3);

        if (num == 1)
        {
            hp1Control.hpShoot();
            if (goalScored)
            {
                hp1Control.hpCelebrate();
                goalScored = false;
            }
        }
        else if (num == 2)
        {
            hp2Control.hpShoot();
            if (goalScored)
            {
                hp2Control.hpCelebrate();
                goalScored = false;
            }
        }
        else if (num == 3)
        {
            hp3Control.hpShoot();
            if (goalScored)
            {
                hp3Control.hpCelebrate();
                goalScored = false;
            }

        }
    }

    public void GoalScored()
    {
        goalScored = true;
    }

There has to be a better way of doing this. I want to call the HockeyPlayerSelection() once per turn. I apologize for my issues, I am pretty new to Unity.

EDIT: This actually seems to be working better now. Just have to tweak a few more things. Are the things you see I could improve?

No need to apologize. I understand sometimes you run into limitations based on the assets you can get. Not having modeling/animation skills myself I run into the same issues. The game logic portion might really be better suited for the “Scripting” forum, but I can see the following:

  1. GoalScored() does not seem to be called anywhere, so there will never be a case where goalScored is true.
  2. When call GetComponent(), I would recommend using GetComponent() rather than the overload using a string parameter.
  3. Implementing timer based functionality is, in my opinion, best implemented using coroutines (Docs here).
  4. Since you are implementing an enum as a way to determine state, go ahead and make it public so that it can be used across your project if it is helpful. Enums are a great way to keep your code descriptive rather than just a true/false, which can get confusing.

I hope that might help out a little bit. Don’t be afraid to ask more questions over in the scripting forum to help figure things out a bit more.

  1. I’m not sure why I included GoalScored(), that is to be used with other scripts.
  2. I actually tried doing it that way at first, but for some reason it wouldn’t work, which is why I stuck with the string parameter.
  3. I have worked with coroutines multiple times in previous assignments, but for this I had issues. I got the player actually to celebrate when using timers, but I tried using coroutines again and he just repeats the shooting animation. I do something like this:
    void Update()
    {
        StartCoroutine(HockeyPlayerTurn());
    }

.................(HockeyPlayerSelection code)

    IEnumerator HockeyPlayerTurn()
    {
        HockeyPlayerSelection();
        yield return new WaitForSeconds(5);
    }

I can’t seem to get that to work how I want it to.

  1. Calling HPShoot() and HPCelebrate() from the HockeyPlayerController script just sets the state equal to a new state. the goalScored just sees if the puck hits the net box collider.

EDIT: Got it working somewhat how I want it. One slight problem is that celebrate is slightly delayed. I got rid of the GoalScored() function and made the goalScored bool public so it can be accessed by the script attached to the net.

In that script attached to the net, this is the function checking if the player scored:

    void OnTriggerEnter(Collider other)
    {
        if (other.tag == "Puck")
        {
            gameController.goalScored = true;
        }
    }

This sets goalScored to true so the player can celebrate. Any idea why it would be delayed?

Honestly couldn’t say what is causing the delay. A lot can depend on how much is going on every frame and the order of execution in Unity.