what reference should I put inside Button void OnClick() in unity - unity3d

When I start to play , it can't jump to the next scene .It also pumps out many error ,one error repeats many times , Coroutine couldn't be started because the the game object 'FadeOut' is inactive!It made me confused since the rawimage is all black and must be inactive before the function onclick.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class ToLoading : MonoBehaviour
{
public Button toLoad;
public RawImage fadeOut;
public Text loading;
// Start is called before the first frame update
void Start() //need to SetActive(false); ? if I set it false already in unity
{
}
// Update is called once per frame
void Update()
{
ButtonClicked();
}
public void ButtonClicked() //
{
fadeOut.gameObject.SetActive(true);
loading.gameObject.SetActive(true);
StartCoroutine(ToNextScene());
}
//going to do: fade out> 2 secoonds > to next scene
private IEnumerator ToNextScene()
{
yield return new WaitForSeconds(2);
SceneManager.LoadScene("SceneForMovingAround");
}
}
My question is : Should I put this script ToLoading to the fadeout rawimage ,or create an empty gameobject then put the script to it .Also , how to solve the problem -fade out error.And last , will it a problem about the code of scene part ?all of your help is appreciated, thanks!

Your ButtonClicked() method is executed every frame, which causes the unwanted behaviour your describe.
Remove the entire Update() method with its body from your file and run your game again.
Add gameObject.SetActive(false); to your ButtonClicked() method to deactivate the button upon clicking.
If you have attached the ButtonClicked() method to the OnClick() event in the inspector as you show in your screenshot then it should work as you would expect.

Related

Why the sound effect does not work in unity

Why the sound effect does not work in unity?
when I try to add a sound effect, it doesn't work
This is my script to move to the next scene using buttons:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class LevelLoader : MonoBehaviour
{
public Animator transition;
public float transitionTime = 1f;
public void LoadNextLevel()
{
StartCoroutine(LoadLevel(SceneManager.GetActiveScene().buildIndex + 1));
}
public AudioClip impact;
IEnumerator LoadLevel(int LevelIndex)
{
transition.SetTrigger("Start");
yield return new WaitForSeconds(transitionTime);
SceneManager.LoadScene(LevelIndex);
AudioSource.PlayClipAtPoint(impact, transform.position);
}
}
You're making the audio play, and then immediately loading in a different scene. The object that is playing the audioclip also unloads.
You could offset the two by making NewGame() a coroutine instead, with a small timed offset between the PlayClipAtPoint() call and the LoadScene() call.
As Alex Leest said, you are loading new scene right after audio play.
When unity scene loading started, all gameobject except DontDestroyOnLoad Object are destroyed.
So, you may add code below to your Audio Manage class.
(Also, you should ensure that your Audio Manager class always has only one instance in your whole program for not cause bugs)
private void Awake()
{
DontDestroyOnLoad(this);
}

How to click on instantiated objects and get points, then link points to score text?

What I want:
I want the player to be able to click on instantiated objects and get points, then have those points show in the score-keeping text.
What I’ve done:
I’m currently using the following “FindGameObjectsWithTag” code to retrieve the buttons that are components of the instantiated prefab objects:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class CPointScore : MonoBehaviour
{
public TextMeshProUGUI CPointsText;
private float ScoreNum;
private GameObject[] CButtonGmeObjsHolder;
private void CTagFinder()
{
CButtonGmeObjsHolder = GameObject.FindGameObjectsWithTag("Ctag");
foreach (GameObject CButtonGmeObj in CButtonGmeObjsHolder)
{
Debug.Log("GmeObj Found");
Button CButton = CButtonGmeObj.GetComponent<Button>();
CButton.onClick.AddListener(AddScore);
}
}
public void AddScore()
{
ScoreNum += 1;
Debug.Log("Point Added # " + ScoreNum);
}
void Start()
{
InvokeRepeating("CTagFinder", 1f, 15.1f);
}
void Update()
{
CPointsText.text = ScoreNum.ToString();
}
}
Because FindGameObjectsWithTag only calls once I have the InvokeRepeating code in start. I have game objects spawning throughout the duration of the game so it needs to be constantly checking for tags.
Issue:
So the code finds the tags, the buttons are able to be clicked, and the score-keeping text updates which is great. The problem is that if I click one tagged button it will register a point for itself and every tagged button currently in the scene that spawned after it. For example, lets say I have 4 spawned objects currently on scene, when the first object spawned is clicked it will add 4 points instead of 1. If the second object spawned is clicked it will add 3 points instead of 1. I would like to have only the tagged button that is clicked register a point.
Question:
What can I change in my code so that only the tagged button that is clicked registers a point?
Thank you
I think there are two things here:
You repeatedly add the listener so you will end up with multiple callbacks when the button is finally clicked.
The repeated FindGameObjectsWithTag is also quite inefficient
Your main issue is the repeated calling.
For each repeated call of CTagFinder you go through all existing buttons and do
CButton.onClick.AddListener(AddScore);
so these existing buttons end up with multiple listeners attached!
You either want to make sure it is only called once per button, e.g. keeping track of those you already did this for:
private readonly HashSet<Button> alreadyRegisteredButtons = new HashSet<Button>();
and then
if(!alreadyRegisteredbuttons.Contains(CButton))
{
CButton.onClick.AddListener(AddScore);
alreadyRegisteredButtons.Add(CButton);
}
or alternatively make sure you remove the callback before you add it like
CButton.onClick.RemoveListener(AddScore);
CButton.onClick.AddListener(AddScore);
In general I would not use FindGameObjectWithTag an poll objects repeatedly. Rather make your code event driven. This would already avoid the issue at all since there would be no repeated attaching of the listener anyway.
I would simply have a dedicated component YourComponent attached to the same GameObject as the buttons and have a global
public static event Action<YourComponent> OnCTSButtonSpawned;
and in this dedicated component do
private void Start()
{
OnCTSButtonSpawned?.Invoke(this);
}
and in your CPointScore listen to this event like
private void Awake()
{
YourComponent.OnCTSButtonSpawned += AttachListener;
}
private void AttachListener(YourComponent component)
{
if(compoenent.TryGetComponent<Button>(out var button))
{
button.onClick.AddListener(AddScore);
}
}
private void AddScore()
{
ScoreNum++;
CPointsText.text = ScoreNum.ToString();
}

How to retrieve the current animation playing from Animator?

I want to check whether an Animation is playing or not so can you help me retrieve the name of the current Animation playing.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AIM : MonoBehaviour
{
public Animator play;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
//Debug.Log(play.GetCurrentAnimatorClipInfo(0).clip.name);
}
}
Animator.GetCurrentAnimatorClipInfo
I tried play.GetCurrentAnimatorClipInfo(0).clip.name but it throws an error :
'AnimatorClipInfo[]' does not contain a definition for 'clip'
It is because you are accessing the array. GetCurrentAnimatorClipInfo(0) returns an array. You need to access the array element to get is info, like this:
play.GetCurrentAnimatorClipInfo(0)[0].clip.name

SyncVar not changing Bool on button press Unity 3d

This is my first networking project. I tried to follow some tutorials and this is what I'm running into: I'm trying to simply change a boolean when a button is clicked. The button is in one scene and the text object down here is in another scene. So i'm running the same network manager in two separate scenes. I realize this isn't conventional but it has to be this way for my project. All I'm looking for right now is for it to change the text, once I understand how that happened, I'm sure I can figure out the rest.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
public class textChanger : NetworkBehaviour
{
Text text;
[SyncVar]
bool change = false;
// Use this for initialization
void Start ()
{
text = gameObject.GetComponent<Text>();
}
// Update is called once per frame
void Update ()
{
if(change)
{
text.text = "it worked";
}
}
[Command]
public void CmdChangeText()
{
change = true;
}
}
If I set "change" to true with a keypress, the code operates exactly as it should, the text changes. But it's not working when i click the button from the other scene. I'm using Networking Hud and the two scenes are in fact connected. But the variable is not updating.
In the first picture, the "Text" gameObject is running the "Text Changer" script. And in the second picture, the button has the generic "Game Object" object running it too. You can see it referenced in the buttons onClick area that's calling the "CmdChangeText" method on the "Text Changer" script.
So in my head, everything looks like it should be working, but it's not. Can someone help?
From the Unity Documentation:
These variables will have their values sychronized from the server to clients
So if you try to set in on a client it won't work. It only can be done using a [Command] like you already did.
You should also check your console output. As far as I know In order to be able to call a Command method, the holding NetworkIdentity has to be set to LocalPlayerAuthority. I always had to put a special class for all commands on the Player object itself in order to make it work.
I know this is maybe not an answer but atleast a workaround:
Instead of waiting for the [SyncVar] you could just directly set the value using a [ClientRpc]:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
public class textChanger : NetworkBehaviour {
Text text;
private bool change = false;
// Use this for initialization
void Start () {
text = gameObject.GetComponent<Text>();
}
// Update is called once per frame
void Update () {
if(change)
{
text.text = "it worked";
}
}
[Command]
public void CmdChangeText()
{
// sets the value on the server
change = true;
RpcChangeText();
}
// This is executed on ALL clients
// SyncVar is no longer needed
[ClientRpc]
private void RpcChangeText()
{
change = true;
}
}

Unity Random is detroying all objects

I'm trying to make the system work so that when a game starts and for each wall, it will either be in the game or not, randomly. Somehow, when I run it, it either keeps or destroys all the walls. How do I fix this?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WallGen : MonoBehaviour{
public GameObject wallObject;
System.Random rnd = new System.Random();
// Use this for initialization
void Start () {
generate();
}
void generate()
{
int number = rnd.Next(1, 5);
if (number == 2)
{
OnDestroy();
}
}
private void OnDestroy(){
Destroy(wallObject);
}
// Update is called once per frame
void Update () {
}
}
Every time you do new System.Random() it is initialised using the clock. Now as the Start function is called at the same time, you get the same value for all the gameobjects. You should keep a single Random instance for all gameobjects and keep using Next on the same instance.
Or the easy solution
just Use Random.Range (1,5) instead of rnd.Next(1, 5);
As a side note, don't use the function name OnDestroy, as it is one of the MonoBehaviour functions in Unity and is called automatically when the attached gameobject is destroyed.