How to access gameobject present in another scene [duplicate] - unity3d

This question already has answers here:
How to pass data (and references) between scenes in Unity
(6 answers)
Closed 4 years ago.
I am creating a simple number guessing game in unity3d.
I want to open a new scene on button click and change the text of a text element present in the loaded scene from the current scene.
I have been able to open new scene on button click but how can i access the text element in other scene so that i can change its text from the current scene.
This is what i have so far but it obviously throws NullReferenceException because i can't access the text element in another scene from current scene.
SceneManager.LoadScene("End Scene");
gameResultText.text = "You Won!!"; //<-------this line throws the exception
gameResultText.color = Color.green;

Better solution I came up with:
Make a script that sets a static string variable. This script must be in your Game scene and will hold the result.
public class ResultTextScript : MonoBehaviour
{
public static string ResultText;
void EndGame(){
if (won){ //if won game
ResultText = "You won!"
}
else //if lost game
{
ResultText = "You lost, try again another time!"
}
//Change scene with SceneManager.LoadScene("");
}
}
Put this script on your result text in the end scene. This script will retrieve the result and display it.
Using UnityEngine.UI;
public class EndGameDisplayResult{
Text text;
OnEnable(){
Text.text = ResultTextScript.ResultText
}
}
This way, it will set the text as soon as the new scene is loaded.
Previous/alternative method:
If you already have the scene open, one option would be to add a script to the "You won!" text which holds a static variable with a reference to itself.
So like this.
public class ResultTextScript : MonoBehaviour
{
public static ResultTextScript Instance;
void Awake(){
if (Instance == null)
Instance = this;
}
}
Then you can call the reference to that GameObject from anywhere in the other scripts, including between scenes.
Like this ResultTextScript.Instance
Note though that you cannot call the reference in the Awake method, as that is where the variable is initialized, you can use it after the Awake methods have been called though.
Basically
Add the ResultTextScript to your Text object in the 'End Scene'
Open the 'End Scene' from the 'Game Scene', for example with your SceneManager approach as you already do.
Ensure that the End Scene has loaded, then say in the script you wish to change the text gameObject go = ResultTextScript.Instance.gameObject

I do not believe there is a way to modify the context or objects of a scene that is not currently open.
public class GameResults {
public static string finalText = "";
}
In your function where you are loading the scene, right before you call load scene you can access that text like so:
GameResults.finalText = "You Win!";
or
GameResults.finalText = "You Lose!";
load your scene, and on your text object give it a script like this:
using UnityEngine;
using UnityEngine.UI;
public class ResultTextScript : MonoBehaviour
{
public Text textResults;
void Start() {
textResults = getComponent<Text>();
if(textResults != null) {
textResults.text = GameResults.finalText;
}
}
}
There are other things you can use as well is, storing the game results in PlayerPrefs and loading the string or int you stored in PlayerPrefs preferences at the start of your end scene. This will help you avoid creating an unnecessary class or static variable.
So Like before you can do:
PlayerPrefs.SetString("GameResults", "You Win!");
or
PlayerPrefs.SetString("GameResults", "You Lose!");
load your scene, and on your text object give it a script like this:
using UnityEngine;
using UnityEngine.UI;
public class ResultTextScript : MonoBehaviour
{
public Text textResults;
void Start() {
textResults = getComponent<Text>();
if(textResults != null) {
textResults.text = PlayerPrefs.GetString("GameResults", "");
}
}
}

Related

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();
}

I'm getting an UnassignedReferenceException when the reference is set. I've seen other questions similar but not exactly the same

As the title says i'm getting the UnassignedReferenceException error for a variable already set. Using ScriptableObjects im working on an inventory system(partially from scratch) and im trying to access the EquipmentUi in another class using a GameObject to hold the prefab containing the Character script. There is no issue with this working to access the Character script as shown in the picture because i can access the name. However, when i try and access the EquipmentUI of that character it gives the error. This isnt the last part i need access to but i have figured out that the UI is the part i cant access, i need the script held in it(which has worked before in another class).
The variable is already assigned, there is no other object in my scene with the same script attached, and the code can access other parts of the script i want access to which is why i made a new post after seeing the other posts and not seeing one that had quite the same issue.
using System.Collections;
using System.Collections.Generic;
using UnitEngine;
[CreateAssetMenu(fileName = "New Character Equipment", menuName = "Inventory/Character/CharacterEquipment)]
public class CharacterEquipmentObject : ScriptableObject
{
[SerializeField]
protected Player user;
[SerializeField]
private GameObject equipmentUser; // Only used to get the user because SOs cant getObject<>
[SerializeField]
public EquipmentObject[] equipment = new EquipmentObject[8];
[SerializeField]
EquipmentDisplay equipmentUI;
//private string[] SlotList = new string[8]{"Helmet", "Shoulders", "Chest", "MainHand", "OffHand", "Ring", "Legs", "Feet"}; may need later
public void start() // must be called in display
{
user = equipmentUser.GetComponent<Player>(); // set the user to be used in other classes\
Debug.Log("user in CEO: " + user.characterName); // this displays fine
Debug.Log("user " + user.characterName + "'s equipment: " + user.EquipmentUI.name); // This is what the editor says is empty when the slot has it assigned
equipmentUI = user.EquipmentUI.GetComponentInChildren<EquipmentDisplay>();
Debug.Log("equipmentUI: " + equipmentUI.name);
}
Thanks for any help in advance.
Player class:
public class Player : Character
{
[SerializeField]
protected Character[] companions = new Character[3];
[SerializeField]
GameObject InventoryUI;
public GameObject EquipmentUI;
bool isSelling = false;
public string characterName;
protected override void Start()
{
InventoryUI.SetActive(false);
EquipmentUI.SetActive(false);
Debug.Log("Player Equipment UI: " + EquipmentUI.GetComponentInChildren<EquipmentDisplay>().name);
base.Start();
}
private void OnTriggerEnter(Collider other)
{
var item = other.GetComponent<InteractItem>();
if(item)
{
characterInventory.addItem(item._item, 1);
useItem(item._item);
}
Destroy(other.gameObject);
}
private void Update()
{
if(Input.GetKeyDown(KeyCode.I))
{
InventoryUI.SetActive(!InventoryUI.activeSelf);
}
if(Input.GetKeyDown(KeyCode.C))
{
EquipmentUI.SetActive(!EquipmentUI.activeSelf);
if(EquipmentUI.activeSelf == true)
{
EquipmentUI.GetComponentInChildren<EquipmentDisplay>().updateEquipmentSlots();
}
}
}
Thanks for all the help on here but i figured out it is a ScriptableObjects issue as i went in and changed the way it is run, even putting the function to edit the users InventoryObject into the player itself it still gave the same issue.
In the player function Update() that does not have the InventoryObject(ScriptableObject) the update runs fine but as soon as the player function EmptySlot() is called from the InventoryObject it sends the same error.
Not entirely sure why this is but SOs are unable to access the GameObject that holds the script for the inventory display in them whatsoever. If anyone has a reason as to why this is I would love to know, but i am going to move on from this particular part and change how it works.
You might be experiencing a bug I have been facing where the element variable info does not update to show variable changes.
To force it to update, cause an error in your code. Switch to unity, when it shows you the error go back and fix it, and see if the variable value changes then.

Unity - how to transfer a set sprite value to different scenes? [duplicate]

This question already has answers here:
How to pass data (and references) between scenes in Unity
(6 answers)
Closed 1 year ago.
I'm new to working in unity and i don't quite understand how to transfer data between scenes. In my case it's the sprites for my inventory when the player picks up an item. here is the code for the items sprites etc.
public class Items : MonoBehaviour
{
public string ItemName;
public string ItemText;
public int itemInList;
public Sprite itemSprite;
public InventorySlot inventorySlot;
public ItemSprites itemSprites;
public Sprite blueberrySprite;
public Sprite cheesecakeSprite;
public Sprite whitebreadSprite;
public Sprite cookiesSprite;
public void Awake()
{
//DontDestroyOnLoad(this);
}
public void gettingID()
{
GameObject Go = new GameObject();
Go.AddComponent<InventorySlot>();
inventorySlot = Go.GetComponent<InventorySlot>();
inventorySlot.itemID = itemInList;
}
public void ItemList()
{
// Cheesecake
if(itemInList == 1)
{
ItemName = "Cheesecake";
ItemText = "A delishious Juicy Cheesecake";
itemSprite = cheesecakeSprite;
}
// White Bread
if (itemInList == 2)
{
ItemName = "White Bread";
ItemText = "a basic white loaf that smells amazing";
itemSprite = whitebreadSprite;
}
// Cookies
if (itemInList == 3)
{
ItemName = "Cookies";
ItemText = "just plain Chocolatechip cookies, still delishious though";
itemSprite = cookiesSprite;
}
// Blueberries
if (itemInList == 4)
{
ItemName = "Blueberries";
ItemText = "Big juicy yummy blueberries!";
itemSprite = blueberrySprite;
Debug.Log(ItemText);
}
}
it works just fine when I'm in the scene where the script exist but when I try to use it in my new scene the image will turn out white(blank). I guess that is because the gameobject is not set anymore in the new scene, but how do i transfer the set gameobject sprite to a new scene? I now that you can make stuff static but if I read correctly then it doesn't work on gameobjects. and Dontdestroyonload doen't seem to work either cus the script is not attached to a gameobject in the scene.
a) You can make your sprite a static property in your class and you should be able to access it from anywhere in unity. You can make a simple DataHolder class to hold this value and other static values in your Game (It need not be a MonoBehaviour). However, you will need to load them somehow (From resources or AssetBundles) into your static Array
b) Check my Answer here on how to properly persist a GameObject in the Game if you are using DontDestroyOnLoad and have it attached to a GameObject in the scene. This should be a good option to use, considering you already have a MonoBehaviour class. Just attach it to a GameObject and Follow the answer in the link to maintain one correct instance of the Component.
c) Consider using a simple ScriptableObject if you want to save these values across multiple Game Sessions. It is a simple data container that helps you save data Independent of Classes.
Follow this link for Unity's Documentation on ScriptableObjects

How to control audio in another scene on/off in unity

I am making a game now, it's almost done. now I am trying to control the audio on and off by button or toggle button.
The problem is, I put my audio source gameobject in the splashscreen that is in the 1st scene. and I put the audio or music button in the Setting scene which is inside the 3rd scene. I already make the c# script to control the audio but when I've tried to insert the AudioSource, but it can't since it's from a different scene. I've tried to put the AudioSource in the same scene but the audio didn't start except I go to settings scene first.
Here is the script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Music : MonoBehaviour
{
static Music instance = null;
public AudioSource Backsound;
private void Awake()
{
if (instance != null)
{
Destroy(gameObject);
}
else
{
instance = this;
GameObject.DontDestroyOnLoad(gameObject);
}
}
public void backsoundOnOff()
{
AudioSource bgsound = Backsound.GetComponent<AudioSource>();
if (bgsound.mute = true){
bgsound.mute = false;
}
else {
bgsound.mute = true;
}
}
}
You have already solved half the problem by using GameObject.DontDestroyOnLoad
The object does indeed exist in both scenes. Now you just need to fetch it.
In the first scene where the created the object, Change the tag of the object. Instead of using one of the exiting tags, create a new tag for it called something such as "MenuMusic". Make sure you assign it after creating it, unity does not assign it automatically
Now, in the 3rd scene, in the game object that needs to access it, create a private field "_music"
in your Start function, add
void Start() {
_music = GameObject.FindGameObjectsWithTag("MenuMusic");
}
You will now have the same instance of Music from scene 1
I would highly recommend referencing the sound script that you have into some sort of game manager. Usually how i work is i have one generic script that controls a multitude of options that i usually call the GameManager. This sets player controls, visual options and sound. From here you can simply set bool whether the player wants the music on and off. If this option wants to change you can reference the GameManager at any point in any script.
//Game Manager code
public void SoundControl(bool soundOff)
{
If(soundOff == true)
{
//Sound Off Control
}else
{
//Sound on Control
}
}
//Reference to game Manager
GameManager manager;
public void TurnOffSound()
{
//Turn sound off through manager
manager =
GameObject.FindGameObjectWithTag("Manager").GetComponent<GameManager>
().SoundControl(true);
}
I find this to be the easiest way to control any options through one script that you can reference anywhere.

Issue with playing background audio for different scenes in unity3d

In my game, I have two scene.
What I want to achieve is if user navigates from one scene to another, background audio specific to each should be played from start(audio length=0)
But all my efforts are in vain.
I tried using 'Pause' Method of audioSound
I tried
create a new game object and assign this scene background score to it and play
destroy gameObject created for another scene if there was any
But it doesn't give the result that I want.
I searched for finding how to play audioClip from start and stop other audioClip playing but didn't find any.
I know I'm not supposed to ask for code on stack overflow but if anyone has achieved this or has some pseudo code request you to provide it
I'm not sure I understand your question properly, since it seems the simplest scenario for background music.
If you really want a change of audioclip in every scene, let's say Scene A must play Clip A and Scene B must play Clip B, and both clips should be played as soon as a scene is loaded, you just need to create a game object in both scenes with an Audio Source component, with the Play On Awake flag active, and then just assign the appropriate clip for the scene (i.e.: assign Clip A in the Audio Clip field of the Audio Source component of Scene A game object, and do the same with Clip B for Scene B).
That's pretty much it.
If you looking at the detail code then you can try this code.
First : Make a script "SoundFxScript.cs" // You can modified as you want
Insert this code :
public class SoundFxScript : MonoBehaviour {
//Background Music
public AudioSource Scene1_Sound;
public AudioSource Scene2_Sound;
// Use this for initialization
void Start () {
PlayBackgroundMusic ();
}
// Update is called once per frame
void Update () {
}
public void PlayBackgroundMusic() {
if (UnityEngine.SceneManagement.SceneManager.GetActiveScene ().name == "Scene1") {
Scene1_SoundPlay();
} else if (UnityEngine.SceneManagement.SceneManager.GetActiveScene ().name == "Scene2") {
Scene2_SoundPlay();
}
}
public void Scene1_SoundPlay() {
Scene1_Sound.Play ();
Scene2_Sound.Stop ();
}
public void Scene2_SoundPlay() {
Scene1_Sound.Stop ();
Scene2_Sound.Play ();
}
// Step Fifth
public void LoadTheScene (string Scenename) {
UnityEngine.SceneManagement.SceneManager.LoadScene (Scenename);
sf.PlayBackgroundMusic ();
}
}
Second : Make Gameobject name = "SoundMusic" at the first scene and add component script SoundFxScript.cs. In gameobject "SoundMusic" you can add you background music for scene1 and scene2.
Third : Make a singleton file Singleton.cs
Insert this code :
public class Singleton : MonoBehaviour {
static Singleton instance = null;
void Start ()
{
if (instance != null)
{
Destroy(gameObject);
return;
}
DontDestroyOnLoad(gameObject);
instance = this;
}
}
Fourth : at gameobject "SoundMusic" add component script "Singleton.cs"
Fifth : How To Call In Another Scene (Load Scene). This method is inside SoundFxScript.cs
Example You have a method to call a load scene. Try this method :
Call it with : LoadTheScene("Scene2") // Call scene2
In here you can call your SoundFxscript.cs Component from any script you have.
Example :
SoundFxScript sf;
sf = GameObject.Find ("SoundMusic").GetComponent<SoundFxScript> ();
And you can use method LoadTheScene to load a new scene and the background music will RePlay again according to the what Scene is it.
That's All.