How can I address the Animator of a prefab from a C# script?
Hi there,
I'm a relative beginner at Unity and C#, but I've been trying to solve this problem for days, have read countless articles and posts and am getting nowhere. Hoping someone here can help.
Here's the overview: I am building an Android AR app using AR Core as a foundation, and have the app set up to instantiate a prefab of an object with an animation. I also have a set of UI buttons that I want the user to be able to tap to pause, play, and reset the animation. The Animator for my object is set up with those three animation states for the object. And I have a C# script written which is intended to connect the UI buttons to activate those animation states.
The basics of the app work fine - the object is instantiated after user input, I can have it either start playing or paused by default using the Animator (verifying that my states work), and the buttons successfully activate methods (verified by checking debug.log). However, I get errors any time I try to make those methods address the Animator for the object. I've tried setting triggers and booleans linked to transitions, and also just telling it to Play the specific animation, but those always throw errors which I'll list below. By googling those errors, I ended up with my current theory is that the issue is related to the fact that the object to be animated is a child of a prefab which gets instantiated, and not an object that exists in the scene from the start. The C# script cannot seem to address the Animator.
So this leads me to my question: How can I address the Animator of a prefab from a C# script?
Below are some screenshots of how this is set up, errors I'm getting and my code. I welcome any suggestions, solutions or questions you may have. Thanks so much for your time!
Structure of Prefab - Attempt 6 is the object that gets instantiated, with Channel Adjuster Prefab being the object that gets animated.
Inspector Panel of Channel Adjuster Prefab object with Animator and C# Script attached. Note that I'm unsure of the "Anim" variable in the script - it feels like this should also be the Animator Controller which is called "anim" in the Animator panel above, but Unity would not allow me to put that there, only the object itself.
Inspector settings for the three buttons - they all look similar to this, referencing the prefab object and then calling on each of their respective methods - Play, Pause and Reset.
Structure of the Animator attached to the object. I have tried setting up the transitions with triggers and booleans, currently they are set up to use the triggers seen in the C# script. I can verify independently that the animations work by changing the default animation. It plays when I have Play as the default, and stays paused when I have Pause as the default.
These are the errors I get for the various attempts at controlling the Animator from the C# script. The Debug.log message verifies that the method runs, but it seems unable to address the Animator.
Below is my code, I realize some of this may seem redundant, but that's because I've tried so many different ways to get this to work. I have also tried each of these independently, with different transition settings for the animations as well.
public class AnimationControls : MonoBehaviour
{
public Animator anim;
public GameObject channelAdjuster;
public Button uiplayButton;
public Button uipauseButton;
public Button uiresetButton;
public void Start()
{
channelAdjuster.SetActive(true);
anim = GetComponent<Animator>();
uiplayButton.onClick.AddListener(Play);
uipauseButton.onClick.AddListener(Pause);
uiresetButton.onClick.AddListener(Reset);
}
public void Pause()
{
anim = GetComponent<Animator>();
Debug.Log("You have clicked the pause button!");
anim.Play("pauseAnimation");
anim.SetTrigger("pauseTrigger");
anim.SetBool("pauseButton", true);
anim.SetBool("playButton", false);
anim.SetBool("resetButton", false);
}
public void Play()
{
anim = GetComponent<Animator>();
Debug.Log("You have clicked the play button!");
anim.Play("playAnimation");
anim.SetTrigger("playTrigger");
anim.SetBool("playButton", true);
anim.SetBool("pauseButton", false);
anim.SetBool("resetButton", false);
}
public void Reset()
{
anim = GetComponent<Animator>();
Debug.Log("You have clicked the reset button!");
anim.Play("resetAnimation");
anim.SetTrigger("resetTrigger");
anim.SetBool("resetButton", true);
anim.SetBool("playButton", false);
anim.SetBool("pauseButton", false);
}
Edit: Sorry if this posted multiple times, I kept getting errors when I went to post. I deleted the extras. Not trying to spam, honest!
Answer by mmccrackenalert · Jul 10, 2020 at 07:14 PM
I ended up solving this, the problem was the way I was trying to identify the prefab and its animator. I had declared it in my script and specified the prefab in the Inspector (as shown above), but I don't think that was matching up with the instantiated version of the prefab that appears in game. And because of that, my methods were not able to address the correct AnimatorController.
So the solution was to go into the AR Controller code where the gameObject itself gets instantiated, call the Animator there, and put my methods afterward.
This was the critical code that made it work, where gameObject refers directly to the instantiated version of the prefab in the AR Controller script:
anim = gameObject.GetComponentInChildren<Animator>();
After this, any attempts to address the Animator via methods, buttons etc. work as intended.
Hope this solution helps someone in the future as I went through literally dozens of forum posts trying to solve this.
Answer by williamfalvo · Jun 25, 2020 at 09:11 AM
I write a few things to try to see when you hit play the application:
1) To Start function don't link itself by a variable, may made a error, just do "gameobject.SetActive(true);"
2)Same things for the animator, take a variable only on Awake or Start and store it only one time
3)Check if gameobject is not active
PS. use the debug to see the status of varibles
Hi, thanks for your reply.
So I'm a bit confused about #1, because if I don't set the gameobject as a variable, I can't define what it is in the inspector. I also get an error if I write it that way. $$anonymous$$aybe I'm not understanding what you mean.
For the animator, the only reason I added it to the other methods is because I get errors when I run it on Start() for some reason it doesn't seem to work. The errors go away when I add that bit to the other methods, but it still doesn't do what I want it to.
I also tried some code I found elsewhere that checks if the gameobject is active, but it didn't do anything so I removed it.
I'm looking into ways of using debug to check the status of the animator.
Answer by ckocank · Apr 08, 2021 at 10:29 AM
I've found the easiest way to solve this problem is create a new button inside the prefab.