I am currently creating some (non-looped) animations in Unity, that should start when a UI button is pressed. I have four buttons for four animations. Currently it works like that: I press button1 then animation1 is playing. If I then press button2 to play animation2, I can click again button1 to start animation1.
The thing is now: animation1 can only be played again, when another animation played before. So my question is: what do I need to change on the following code to let for example animation1 play every time button1 is pressed?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class MultiAnimations : MonoBehaviour {
public Animator cubeAnimator;
public bool anim1;
public bool anim2;
public bool anim3;
public bool anim4;
// Use this for initialization
void Start () {
cubeAnimator.enabled = false;
anim1 = false;
anim2 = false;
anim3 = false;
anim4 = false;
}
public void anim1Clicked ()
{
cubeAnimator.enabled = true;
anim1 = true;
anim2 = false;
anim3 = false;
anim4 = false;
if (anim1 == true)
{
cubeAnimator.SetBool("anim1", true);
cubeAnimator.SetBool("anim2", false);
cubeAnimator.SetBool("anim3", false);
cubeAnimator.SetBool("anim4", false);
}
}
public void anim2Clicked ()
{
cubeAnimator.enabled = true;
anim1 = false;
anim2 = true;
anim3 = false;
anim4 = false;
if (anim2 == true)
{
cubeAnimator.SetBool("anim1", false);
cubeAnimator.SetBool("anim2", true);
cubeAnimator.SetBool("anim3", false);
cubeAnimator.SetBool("anim4", false);
}
}
public void anim3Clicked()
{
cubeAnimator.enabled = true;
anim1 = false;
anim2 = false;
anim3 = true;
anim4 = false;
if (anim3 == true)
{
cubeAnimator.SetBool("anim1", false);
cubeAnimator.SetBool("anim2", false);
cubeAnimator.SetBool("anim3", true);
cubeAnimator.SetBool("anim4", false);
}
}
public void anim4Clicked()
{
cubeAnimator.enabled = true;
anim1 = false;
anim2 = false;
anim3 = false;
anim4 = true;
if (anim4 == true)
{
cubeAnimator.SetBool("anim1", false);
cubeAnimator.SetBool("anim2", false);
cubeAnimator.SetBool("anim3", false);
cubeAnimator.SetBool("anim4", true);
}
}
}
The animations are connected with transitions and bools. I oriented myself on this tutorial: https://www.youtube.com/watch?v=Q_5FhXF9a3k
SOLVED: The code provided in the YouTube tutorial is pretty circuitously. You can save a lot lines of code to simple write it like that:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MultiAnimations : MonoBehaviour {
public Animator cubeAnimator;
// Play Animation 1 - Copy this for the other animations
public void playAnimation1 ()
{
cubeAnimator.Play("animation1", -1, 0f);
}
}
Where's your update method? How are you calling these void methods?
Put an update method with conditions to check which button is clicked. So something like this:
void Update()
{
if (Input.GetKeyDown("space"))
{
//Play animation.
}
}
EDIT: Ok, when you press the button to play the first animation, and you press that button again, the animation is already true, so the animation already played. You need to set it to false after you're done playing the animation. Which is the reason why it plays again when you press other buttons.
EDIT: Try this style
public void anim1Clicked()
{
animation.Play("anim1");
}
Related
Im working on an 2d local multiplayer platformer Game. In the game are obstacles (Spikes), when the player collides with them the player will die. I want the Players of the game to decide if they would like to enable or disable the Spikes (the link leads to an image that will help understanding my problem)by pressing a Key. I already wrote a script for that and added it to my dontdestroyOnLoad GameManager. So all my spikes I built are the same Prefabs. My idea was to disable the main Prefab from the Project folder to disable all the spikes in every scene, until you press a Key to reactivate them again. The Problem is, that only the texture itself in the Project Panel disables and not the Spikes Prefabs in the Hierarchy, because the prefabs turn into regular gameObjects. How can I fix this?
My Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//using System.Threading;
public class DisableSpikes : MonoBehaviour
{
[Header("Spikes")]
public KeyCode disableSpikes;
public float time;
public GameObject prefabSpikes;
public bool toggleSpikes = true;
[Header("Player Green")]
public KeyCode disableGreen;
public GameObject prefabGreen;
public bool toggleGreen = true;
[Header("Reset Score")]
public KeyCode resetScore;
// Start is called before the first frame update
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(disableSpikes) && toggleSpikes == true)
{
prefabSpikes.SetActive(false);
Debug.Log("Disable");
//Thread.Sleep(1000);
Invoke("SetFalse", time);
}
if (Input.GetKeyDown(disableSpikes) && toggleSpikes == false)
{
prefabSpikes.SetActive(true);
Debug.Log("Anable");
//Thread.Sleep(1000);
Invoke("SetTrue", time);
}
if (Input.GetKeyDown(disableGreen) && toggleGreen == true)
{
prefabGreen.SetActive(false);
Debug.Log("Disable");
//Thread.Sleep(1000);
Invoke("SetFalse", time);
}
if (Input.GetKeyDown(disableGreen) && toggleGreen == false)
{
prefabGreen.SetActive(true);
Debug.Log("Anable");
//Thread.Sleep(1000);
Invoke("SetTrue", time);
}
if (Input.GetKeyDown(resetScore))
{
ScoreScriptBlue.scoreValueBlue = 0;
ScoreScriptRed.scoreValueRed = 0;
ScoreScriptGreen.scoreValueGreen = 0;
RoundScript.scoreValueRound = 0;
TimeScript.scoreValueTime = 0;
}
}
public void SetFalse()
{
toggleGreen = false;
toggleSpikes = false;
}
public void SetTrue()
{
toggleGreen = true;
toggleSpikes = true;
}
}
void Update() {
if (Input.GetKeyDown(disableSpikes) && toggleSpikes == true){
// show
// renderer.enabled = true;
gameObject.GetComponent<Renderer>().enabled = true;
}
if (Input.GetKeyDown(disableSpikes) && toggleSpikes == false) {
// hide
// renderer.enabled = false;
gameObject.GetComponent<Renderer>().enabled = false;
}
}
I have troubles trying to make two different actions on trigger enter, its same name tag so I have no idea how to do it. For example I have ground button which has function, etc on first trigger enter it is disabling rotation of some other object, on second trigger enter it enables rotation back.Tried with bools but its not working well, Im thinking about int, but Im not sure. Thanks for any help, Im new in coding.
Heres the code;
if (other.gameObject.CompareTag("Player"))
{
var buttonRenderer = button.GetComponent<Renderer>();
buttonRenderer.material.SetColor("_Color", Color.red);
Rotationplane.isRotated = false;
}
EDIT:
#derHugoDont know mate, lost my mind...but finally I managed to do it over the if statements, I have no idea does it effect the performance. Heres code if someone has the same problem. Opening the bottle of whiskey...done for today.
private void FixedUpdate()
{
if(firstCollisionDone == true)
{
firstCollisionAavaliable = false;
secondCollisionAvaliable = true;
}
if(secondCollisionDone == true)
{
secondCollisionAvaliable = false;
firstCollisionAavaliable = true;
}
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("Player"))
{
if(firstCollisionAavaliable == true)
{
var buttonRenderer = button.GetComponent<Renderer>();
buttonRenderer.material.SetColor("_Color", Color.red);
Rotationplane.isRotated = false;
firstCollision = true;
}
if(secondCollisionAvaliable == true)
{
var buttonRenderer = button.GetComponent<Renderer>();
buttonRenderer.material.SetColor("_Color", Color.white);
Rotationplane.isRotated = true;
secondCollision = true;
}
}
}
private void OnTriggerExit(Collider other)
{
if (other.gameObject.CompareTag("Player"))
{
if(firstCollision == true)
{
firstCollisionDone = true;
firstCollision = false;
firstCollisionAavaliable = false;
secondCollisionDone = false;
}
if(secondCollision == true)
{
secondCollisionDone = true;
secondCollision = false;
secondCollisionAvaliable = false;
firstCollisionDone = false;
}
}
}
}```
I assume the flag you want to change is the Rotationplane.isRotated.
You could simply invert it's state like e.g.
if (other.gameObject.CompareTag("Player"))
{
var buttonRenderer = button.GetComponent<Renderer>();
buttonRenderer.material.SetColor("_Color", Color.red);
Rotationplane.isRotated = !Rotationplane.isRotated;
}
I have 4 buttons and each of this button activates an image (Please see the link - Stacking). One script is attached to all the 4 buttons. There are 4 placeholders. I am using an approach of stack to stack images one after another. That is, if the first slot is empty, the image goes there. The button has two functions, to add image and to remove image. If the first slot's image is removed, the second slot's image is placed on 1st slot and third slot's image is placed on 2nd slot.
How do I add push and pop to this scenario?
Edit: I tried with List but I am lost. Could there be a way simpler approach?
public class ActivateStackImages : MonoBehaviour {
public GameObject ImageGameObject_To_Add; //Each instance of this script has unique image
public Vector3[] PositionOfImages; //4 positions
private ActivateStackImages [] activateImages; //Script added to 4 buttons
public bool Slot_1_Filled = false;
public bool Slot_2_Filled = false;
public bool Slot_3_Filled = false;
public bool Slot_4_Filled = false;
public bool isImageAdded = false;
private void Awake () {
activateImages= FindObjectsOfType (typeof (ActivateStackImages )) as ActivateStackImages [];
}
public void ActivateGraph () {
if (this.isImageAdded == false) {
for (int i = 0; i < activateImages.Length; i++) {
if (activateImages[i].Slot_1_Filled == false) {
ImageGameObject_To_Add.SetActive (true);
ImageGameObject_To_Add.GetComponent<RectTransform> ().anchoredPosition = PositionOfImages[0];
activateImages[i].Slot_1_Filled = true;
} else if (activateImages[i].Slot_2_Filled == false) {
ImageGameObject_To_Add.SetActive (true);
ImageGameObject_To_Add.GetComponent<RectTransform> ().anchoredPosition = PositionOfImages[2];
activateImages[i].Slot_2_Filled = true;
} else if (activateImages[i].Slot_3_Filled == false) {
ImageGameObject_To_Add.SetActive (true);
ImageGameObject_To_Add.GetComponent<RectTransform> ().anchoredPosition = PositionOfImages[2];
activateImages[i].Slot_3_Filled = true;
} else if (activateImages[i].Slot_4_Filled == false) {
ImageGameObject_To_Add.SetActive (true);
ImageGameObject_To_Add.GetComponent<RectTransform> ().anchoredPosition = PositionOfImages[3];
activateImages[i].Slot_4_Filled = true;
}
}
this.isImageAdded = true;
} else if (this.isImageAdded == true) {
ImageGameObject_To_Add.SetActive (false);
}
}
}
Stacking
I have 26 images (GameObjects) where each image has a button.
If a button is clicked, it plays a sound and animation.
However, I have a problem. I have tried to prevent this, but if I perform a multi touch / stress test, some buttons cannot be clicked anymore. If I click slowly it works fine, but if I perform multiple clicks on a button then it will fail/can't be clicked anymore. Any idea why?
My source code :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using DG.Tweening;
[RequireComponent(typeof(AudioSource))]
public class z_abcLetter_Main : MonoBehaviour {
public GameObject InputData;
public List<GameObject> List_uGUI;
private List<Sprite> List_Sprite;
private List<AudioClip> List_AudioClip;
private int currentIndex;
private GameObject currentGameObject;
private GameObject parentGameObject;
// Use this for initialization
void Start () {
List_Sprite = InputData.GetComponent<z_abcLetter_InputData>().Daftar_element;
List_AudioClip = InputData.GetComponent<z_abcLetter_InputData>().Daftar_Suara;
int total_element = List_Sprite.Count;
for (int i = 0; i < total_element; i++)
{
List_uGUI[i].GetComponent<UnityEngine.UI.Image>().sprite = List_Sprite[i];
List_uGUI[i].GetComponent<UnityEngine.UI.Image>().preserveAspect = true;
}
}
public void onButtonClick()
{
currentIndex = int.Parse(EventSystem.current.currentSelectedGameObject.transform.parent.name.ToString());
currentGameObject = EventSystem.current.currentSelectedGameObject;
parentGameObject = currentGameObject.transform.parent.gameObject;
StartCoroutine(PlaySoundWithAnimation());
}
IEnumerator PlaySoundWithAnimation()
{
currentGameObject.GetComponent<UnityEngine.UI.Button>().enabled = false;
currentGameObject.GetComponent<UnityEngine.UI.Button>().interactable = false;
AudioSource audio = GetComponent<AudioSource>();
audio.clip = List_AudioClip[currentIndex];
audio.Play();
parentGameObject.transform.DOPunchScale(new Vector3(2, 2, 0), 1, 1, 1);
yield return new WaitForSeconds(1.5f);
currentGameObject.GetComponent<UnityEngine.UI.Button>().enabled = true;
currentGameObject.GetComponent<UnityEngine.UI.Button>().interactable = true;
}
// Update is called once per frame
void Update () {
}
}
Your code seems to behave as expected. But for fast multiple clicks, you need to reset your coroutine.
To do that, you need a reference to PlaySoundWithAnimation.
private IEnumerator coroutine;
void Start()
{
coroutine = PlaySoundWithAnimation();
}
public void onButtonClick()
{
StopCoroutine(coroutine);
// you also need to stop audio here
//...
StartCoroutine(coroutine);
}
see: https://docs.unity3d.com/ScriptReference/MonoBehaviour.StopCoroutine.html
The reason some buttons are not responding is because the coroutines are stacked and they're all waiting for yield return new WaitForSeconds(1.5f);
see: https://answers.unity.com/questions/309613/calling-startcoroutine-multiple-times-seems-to-sta.html
Note:
If you don't want to stop the clip, consider audio.clip.length before enabling the buttons.
I attached a script to my canvas called PauseMenu which runs once and pauses initially and resumes but never pauses again when clicking escape. Here is my code:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
#if UNITY_EDITOR
using UnityEditor;
#endif
using RTS;
public class PauseMenu : MonoBehaviour {
Canvas canvas;
private Player player;
public Button Button2;
void Start()
{
Debug.Log ("asdf");
player = transform.root.GetComponent< Player >();
canvas = GetComponent<Canvas>();
canvas.enabled = false;
ResourceManager.MenuOpen = false;
Button2.GetComponent<Button>().onClick.AddListener(() => { Resume();});
}
void Update()
{
Debug.Log ("Jake");
if(Input.GetKeyDown(KeyCode.Escape) && ResourceManager.MenuOpen == false) Pause();
//Button2.GetComponent<Button>().onClick.AddListener(() => { Resume();});
//if(Input.GetKeyDown(KeyCode.Escape)) Pause();
//if(Input.GetKeyDown(KeyCode.Escape) && ResourceManager.MenuOpen == true) Resume();
}
public void Pause()
{
//if(Event.current.type == KeyCode.Escape)Debug.Log ("as12d12f2");
//if(Event.current.type == EventType.KeyDown)Debug.Log ("as12d12f2");
Debug.Log ("asdf2");
canvas.enabled = true;
Time.timeScale = 0.0f;
if(player) player.GetComponent< UserInput >().enabled = false;
//canvas.enabled = !canvas.enabled;
//Time.timeScale = Time.timeScale == 0 ? 1 : 0;
//ResourceManager.MenuOpen = true;
}
public void ResumePublic(){
Resume ();
}
private void Resume() {
Debug.Log ("asdf1");
Time.timeScale = 1.0f;
GetComponent< PauseMenu >().enabled = false;
if(player) player.GetComponent< UserInput >().enabled = true;
Cursor.visible = false;
canvas.enabled = false;
//Screen.showCursor = false;
//GetComponent<UserInput > ().enabled = true;
Debug.Log ("a2323sdf1");
ResourceManager.MenuOpen = false;
Button2.GetComponent<Button>().onClick.AddListener(() => { Resume();});
}
public void Quit()
{
#if UNITY_EDITOR
EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
}
}
What is happening is that this is attached to the parent object canvas which has the child object panel which has child objects button1,2and3 . And when the user presses escape the menu should pop up, pause time(which it does) and resume when either the resume button is clicked (button2) or escape is pressed however this only works once and the code no longer runs (ie the update procedure is no longer running) - please can someone help me fix this issue.
In your Resume() you set the PauseMenu Component to false
GetComponent< PauseMenu >().enabled = false;
But you did nothing to turn back that component in your pause function. So the script get disable after the first toggle and is never running again.
Could this be the problem?