I have a mecanim character that I have set up animations for in the animator. I added a 4 triggers to trigger the different animations. My question is, what is the best way to trigger the run animation when a the left/right keyboard button is pressed? What I have now is that the animations keeps getting triggered over and over again because I have the code in an Update(). What can I do so it only gets triggered once and loops through that animation?
I have this working:
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(Animator))]
public class AnimationController : MonoBehaviour {
Animator animator;
bool running = false;
void Start() {
animator = GetComponent<Animator>();
}
// Update is called once per frame
void Update () {
if(Input.GetButton("Horizontal")) {
if(!running) {
animator.SetTrigger("Run");
running = true;
}
}
if(Input.GetButtonUp("Horizontal")) {
animator.SetTrigger("Idle");
running = false;
}
}
}
But it seems like it is more that what is needed to accomplish this task, especially as the actions and animations list grows.
animator.SetTrigger got an inner boolean that becomes true when you enter the state and becomes false when it comes out of the state so there is no need for bools and if you checkHas Exit Time property in out transition event it would wait for the animation to finish here you can see it in the docs
Related
I have a button that is supposed to switch beetween light and dark mode in my game by running the method "ToggleTheme" inside the ObjectTheme script, which all the objects that I want to be affected by light/dark mode have. ToggleTheme just changes the boolean "DarkMode", since all the objects' transitions use this DarkMode boolean. It all works fine if I just assign the objects and select ObjectTheme.ToggleTheme, but if I assign the objects' prefabs and select ObjectTheme.ToggleTheme I get the warning "Animation is not playing an AnimatorController" on button press. Is there any way around this, because assigning every object in every scene would just be to impractical and one of the objects has up to 30 copies in every level of the game?
P.S. I know It probably would have been easier if I just used a toggle instead of a button, but I'm new to Unity and I just couldn't get the toggle to work how I wanted it, so I'm using a button instead.
Here is the ObjectTheme script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjectTheme : MonoBehaviour
{
public Animator animator;
// Start is called before the first frame update
void Start()
{
animator = GetComponent<Animator>();
}
// Update is called once per frame
public void ToggleTheme()
{
if(animator.GetBool("DarkMode") == true)
{
animator.SetBool("DarkMode", false);
}
else
{
animator.SetBool("DarkMode", true);
}
}
}
**Edited after Jonatan's comments below.
I understand the desire to just assign the prefab as the button's event target. But the prefab itself is in some sense also just an instance that is just not living in the scene. While in edit mode, all changes in the prefab itself will reflect in the scene instances. But when you are in play mode (runtime) the prefab instances in the scene will no longer automatically update themselves with changes in the prefab file.
In this case, we are trying to set a bool value on an Animator component, but the Animator on the prefab is not really playing - only the Animators on the scene instances are playing. That is why you get the 'not playing' warning.
One option to solve the issue could be something like the following.
First add a script to the button that has a function that can be hooked up with your button's OnClick() UnityEvent. The script will look for instances of another script, which is present on all the objects that should react to dark mode state. This other script could be your ObjectTheme script but here I call it DarkModeReceiver. When the button triggers the function, the script will simply call a function on all the script instances stored in its array.
//Put this script on the Button,
//and hook up the Button's OnClick event with the OnButtonClicked() function
using UnityEngine;
public class DarkModeHandler : MonoBehaviour
{
static bool isDarkMode;
public static bool IsDarkMode => isDarkmode;//Public get property
//Make your Button call this function in its OnClick() event
public void OnButtonClicked()
{
isDarkMode = !isDarkMode;//Toggle bool
SendIsDarkMode();
}
//Alternatively, if you choose to use a Toggle instead
//you could hook this function up with the Toggle's OnValueChanged(Boolean) event
//with the dynamic bool of that event.
public void OnToggleValueChanged(bool isToggledOn)
{
isDarkMode = isToggledOn;
SendIsDarkMode();
}
void SendIsDarkMode()
{
var darkModeReceivers = FindObjectsOfType<DarkModeReceiver>(true);
foreach (var receiver in darkModeReceivers)
{
receiver.SetIsDarkMode(isDarkMode);
}
}
}
And then the receiving script (attached on all the game objects / prefabs that should react to dark mode state) could be something like this (or a modified version of your ObjectTheme script).
using UnityEngine;
public class DarkModeReceiver : MonoBehaviour
{
Animator myAnimator;
void Awake()
{
myAnimator = GetComponent<Animator>();
}
void Start()
{
//Ensure that our state is in sync with the DarkModeHandler
SetIsDarkMode(DarkModeHandler.IsDarkMode);
}
public void SetIsDarkMode(bool isDarkMode)
{
myAnimator.SetBool("DarkMode", isDarkMode);
}
}
Alternatively, you could do something where the DarkModeReceivers/ObjectThemes register themselves on the DarkModeHandler on their Start() and unregister themselves again on their OnDestroy() - for example by subscribing to an event. Then the DarkModeHandler wouldn't have to look for receivers every time the button is clicked.
how do I make it that "Player walks into a trigger and an animation plays once and if the player walks into the same trigger nothing will happen" There are no videos on youtube nor other websites that I know that will explain how to do this.
Im also New to unity and am not the best when doing animations
[EDITED SCRIPT, FIXED ERRORS]
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// You should make the name TheSecretWallScript
// This is the general format for classes in C#
public class Thesecretwallscript : MonoBehaviour
{
// needed to change this.
[SerializeField] private Animator anim;
// Start is called before the first frame update
private void OnTriggerEnter(Collider other)
{
// Disables trigger so it doesn't trigger again
other.enabled = false;
// Triggers animation, "AnimTrigger" should refer to the trigger you set
// in your animators settings
Animator anim = GetComponent<Animator>();
anim.SetTrigger("AnimTrigger");
}
}
You need to add a trigger for your animation in the player's Animation Controller.
You could disable the trigger once you walk into it by doing:
private void OnTriggerEnter(Collider other)
{
// Disables trigger so it doesn't trigger again
other.enabled = false;
// Triggers animation, "AnimTrigger" should refer to the trigger you set
// in your animators settings
Animator anim = GetComponent<Animator>();
anim.SetTrigger("AnimTrigger");
}
Makes the animation trigger and destroys the trigger after being touched by the player
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// You should make the name TheSecretWallScript
// This is the general format for classes in C#
public class Thesecretwallscript : MonoBehaviour
{
// needed to change this.
[SerializeField] private Animator anim;
// Start is called before the first frame update
private void OnTriggerEnter(Collider other)
{
// Disables trigger so it doesn't trigger again
other.enabled = false;
// Triggers animation, "AnimTrigger" should refer to the trigger you set
// in your animators settings
Animator anim = GetComponent<Animator>();
anim.SetTrigger("AnimTrigger");
}
}
You are destroying the trigger even before the animation trigger happens.
Try doing the anim.getcomponent in the start method and animation.settrigger and in the next line other enabled =false.
Something like that
Hi guys I'm pretty new to unity and I am trying to make an animation where some of the meshes within a prefab only appear ~halfway through the animation.
I have added an event at the desired time within animator but I cant figure out exactly what code I need to execute to turn the mesh renderer on.
So far I have this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class toggleMesh : MonoBehaviour
{
public Renderer rend;
// Start is called before the first frame update
void Start()
{
rend = GetComponent<Renderer>();
rend.enabled = false;
}
// Update is called once per frame
public void toggleMeshR()
{
rend.enabled = true;
}
}
Any help with this would be appreciated
If you have an animation as you said, put an animation event on the frame(time) you desire to call your toggleMeshR().
When you right click on timeline of animation, you can create animation events.
More information: https://docs.unity3d.com/Manual/script-AnimationWindowEvent.html
I created an animator called "m4a4animator". Inside it, the main function is called "idle" (nothing), and other 2 states: "shoot" (mouse0) and "reload" (R). These 2 animation states are transitioned to "idle". Now, everything is working... but the only problem I have is this: if I am in the middle of reloading and and press mouse0 (shoot), the animation running state immediately changes to shoot... but I want to block that.
Now, the question: How can I stop CERTAIN animation changes while an animation is running?
Here is my animator
And here is my script:
using UnityEngine;
using System.Collections;
public class m4a4 : MonoBehaviour {
public Animator m4a4animator;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if (Input.GetKeyDown (KeyCode.R)) {
m4a4animator.Play("reload");
}
if (Input.GetMouseButton(0)) {
m4a4animator.Play("shoot");
}
}
}
For the legacy Animation system, Animation.IsPlaying("TheAnimatonClipName) is used to check if the animation clip is playing.
For the new Mechanim Animator system, you have to check if both anim.GetCurrentAnimatorStateInfo(animLayer).IsName(stateName) and anim.GetCurrentAnimatorStateInfo(animLayer).normalizedTime < 1.0f) are true. If they are then animation name is currently playing.
This can be simplified like the function like the Animation.IsPlaying function above.
bool isPlaying(Animator anim, string stateName)
{
if (anim.GetCurrentAnimatorStateInfo(animLayer).IsName(stateName) &&
anim.GetCurrentAnimatorStateInfo(animLayer).normalizedTime < 1.0f)
return true;
else
return false;
}
Now, everything is working... but the only problem I have is this: if
I am in the middle of reloading and and press mouse0 (shoot), the
animation running state immediately changes to shoot... but I want to
block that.
When the shoot button is pressed, check if the "reload" animation is playing. If it is, don't shoot.
public Animator m4a4animator;
int animLayer = 0;
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.R))
{
m4a4animator.Play("reload");
}
//Make sure we're not reloading before playing "shoot" animation
if (Input.GetMouseButton(0) && !isPlaying(m4a4animator, "reload"))
{
m4a4animator.Play("shoot");
}
}
bool isPlaying(Animator anim, string stateName)
{
if (anim.GetCurrentAnimatorStateInfo(animLayer).IsName(stateName) &&
anim.GetCurrentAnimatorStateInfo(animLayer).normalizedTime < 1.0f)
return true;
else
return false;
}
If you need to wait for the "reload" animation to finish playing before playing the "shoot" animation then use a coroutine. This post described how to do so.
There are other threads about that: https://answers.unity.com/questions/362629/how-can-i-check-if-an-animation-is-being-played-or.html
if (this.animator.GetCurrentAnimatorStateInfo(0).IsName("YourAnimationName"))
{
//your code here
}
this tells you if you are in a certain state.
Animator.GetCurrentAnimatorStateInfo(0).normalizedTime
this give you the normalized time of the animation: https://docs.unity3d.com/ScriptReference/AnimationState-normalizedTime.html
Try to play with those function, I hope that solve your problem