I want to save the attribute of a gameObject but in runtime without Serializing anything. This I want to do when the object is selected. That way when I touch the paint button for example, paint the object that was selected.
What I tested so far is the use of the event OnMouseDown that is in the GameObject script and I also made another empty GameObject with the script that saves the attribute, but I can't save for example the name of the selected object. Although it shows it in the Debug.Log but returns log at the time of assigning it.
I applied the same logic in a new project with ObjSelect, SelectScript, ButtonsEventsScript scripts with the same prefabs and GameObjects but without Vuforia and it worked great.
Repository: https://github.com/emicalvacho/MapaMentalAR
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjSelect : MonoBehaviour
{
private string nombre;
void OnEnable() {
SelectScript.OnName += HandlerOnName;
}
void OnDisable() {
SelectScript.OnName -= HandlerOnName;
}
void HandlerOnName(string str){
Debug.Log("NOMBRE DEL OBJETO SETEADO: " + str);
nombre = str;
}
public string getNombre(){
return nombre;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class SelectScript : MonoBehaviour
{
public GameObject cube;
private GameObject findObj;
public GameObject gameObjectManager;
public delegate void _OnName (string str);
public static event _OnName OnName;
private bool band;
public void OnMouseDown()
{
if(OnName != null)
OnName(cube.name);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ButtonsEventsScript : MonoBehaviour
{
public ObjSelect script;
private GameObject obj;
public void ChangeColor(){
Debug.Log("OBJETO A PINTAR ES: " + script.getNombre());
}
}
I don't have any error messages, but it returns a null instead of the name of the selected GameObject.
If anyone has any other way of doing this, I ask you to help me for more than a week that I've been stuck. I already asked in other forums and nobody gives me a solution or a way to solve.
Related
I'm making a subway simulator, I want the StationBox to always have the Created tag after pressing a button, but it goes back to the previous value when the game is restarted, how can I solve this?
Update: I will have more than 1 station, I would like to make a universal script for all
using System;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
public class AddStation : MonoBehaviour
{
public InputField Input;
public Text StationName;
public GameObject Button;
public GameObject CreateStation;
public GameObject StationBox;
public void OnMouseDown(){
if (Input.text != ""){
CreateStation.SetActive(false);
StationName.text = Input.text.ToString();
StationBox.tag = "Created";
}
}
}
If I understand correctly, you are trying to find a way to save game data so as not to lose it when you restart the game.
To do this the most practical solution is PlayerPrefs.
You can find a lot of documentation online because it's a simple concept.
However for your problem you can do this:
public void OnMouseDown(){
if (Input.text != ""){
CreateStation.SetActive(false);
StationName.text = Input.text.ToString();
StationBox.tag = "Created”;
PlayerPrefs.SetInt(“Created”, 1);
}
}
void Start()
{
if (PlayerPrefs.HasKey(“Created”))
if (PlayerPrefs.GetInt(“Created”) == 1)
StationBox.tag = "Created”;
}
I am making a game where, if the catcher gets destroyed by the object, the game over screen is triggered. All that seems to occur is that there is a giant game over the screen at the beginning of when I play, while the game is running in the background. For some reason, the game does not seem to call in the game over screen only on collision.
This is the script I am using for my catcher, where it collides, disappears, and then the game over screen is set up to be triggered.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CatcherDestroy : MonoBehaviour
{
public GameOverScreen GameOverScreen;
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Catcher"))
{
Destroy(collision.gameObject);
GameOverScreen;
}
}
}
and this is the code for my GameOverScreen.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GameOverScreen : MonoBehaviour
{
public Text pointsText;
public void Setup(int score)
{
gameObject.SetActive(true);
pointsText.text = "Score:" + score.ToString();
}
}
You are not calling anything when an object collides, you're just listing the reference of the object. You'll need to call the function that you have exposed. Without calling a method from the script reference, no code will be run. Edit your first snippet as follows:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CatcherDestroy : MonoBehaviour
{
public GameOverScreen GameOverScreen;
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Catcher"))
{
Destroy(collision.gameObject);
GameOverScreen.Setup();
}
}
}
The other option would be to move the code in Setup to Awake or Start or OnEnable, then instead of calling the function in the collision, you just need to set it as active.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CatcherDestroy : MonoBehaviour
{
public GameOverScreen GameOverScreen;
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Catcher"))
{
Destroy(collision.gameObject);
GameOverScreen.score = theScore;
GameOverScreen.gameObject.SetActive(true);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GameOverScreen : MonoBehaviour
{
public Text pointsText;
public int score;
private void OnEnable()
{
pointsText.text = "Score:" + score.ToString();
}
}
The one issue is you'll need to pass in the score parameter which I do not see in your script CatcherDestroy.
I'm making a 2d game on unity, and I have a script that saves my coin value, but it doesn't save when I open the after getting more coins.
This is my script that saves the data:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Coin : MonoBehaviour
{
private void OnTriggerEnter2D(Collider2D other)
{
if(other.gameObject.tag == "Player")
{
PlayerPrefsManager.coins += 1;
PlayerPrefsManager.UpdateCoins();
}
}
}
And my script that displays the data:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class CoinsDisplay : MonoBehaviour
{
private Text text;
void Start()
{
text = GetComponent<Text>();
}
void Update()
{
string[] temp = text.text.Split('X');
text.text = temp[0] + "X: " + PlayerPrefsManager.coins;
}
}
player prefs manager script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerPrefsManager : MonoBehaviour
{
public const string Coins = "Coins";
public static int coins;
void Start()
{
coins = PlayerPrefs.GetInt("Coins");
}
public static void UpdateCoins()
{
PlayerPrefs.SetInt("Coins", coins);
coins = PlayerPrefs.GetInt("Coins");
PlayerPrefs.Save();
}
}
Your code should work fine, but I guess you've forgotten to assign your PlayerPrefsManager script to a gameObject. Cause if its Start doesn't execute, we will get the same result.
By the way, use your const string 'Coins', you've just declared it but you're not using it in these calls: SetInt, GetInt.
And try to update PlayerPrefs (PlayerPrefs.Save()) less often, since it writes data on the disk and is a little expensive to do it each time your coin's value changes. You can have a method called 'save' on your prefManager to only get called when you're done with the level, mission is complete etc... , or if you don't want to lose the data, call it less often, like once in each 10 seconds (take a look at Invokerepeating)
Basically, I have this Text (scoreText) which is in my "Menu" scene so hence I have initiated it in GameControlMenu.cs, however, I'm trying to change its text from my other script GameControl.cs whilst I'm currently on my "Main" scene.
GameControlMenu.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class GameControlMenu : MonoBehaviour
{
public static GameControlMenu instanceMenu;
public Text scoreText;
void Start()
{
//does stuff but not important to question
}
}
GameControl.cs:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
public class GameControl : MonoBehaviour
{
public static GameControl instance;
public int score = 5;
void Start()
{
GameControlMenu.instanceMenu.scoreText.text = "PREVIOUS SCORE: " + score;
}
}
This setup works for a couple of my other variables which I'm accessing from another file but for whatever reason this just keeps throwing me the error: NullReferenceException: Object reference not set to an instance of an object
Any help is appreciated :)
You can't do GameControlMenu.instanceMenu... since GameControlMenu is in another scene as you described, and the instance isn't in the current scene.
But what you can do is to store the value somewhere first, and let the GameControlMenu use it when the other scene loads like so:
GameControlMenu.cs
public class GameControlMenu : MonoBehaviour
{
public static GameControlMenu instanceMenu;
public static string StuffToShowOnScoreText { get; set; }
public Text scoreText;
void Awake()
{
// So that it loads the text on start
scoreText.text = StuffToShowOnScoreText;
// ...
}
}
GameControl.cs
public class GameControl : MonoBehaviour
{
public static GameControl instance;
public int score = 5;
void Start()
{
// Store the value
GameControlMenu.StuffToShowOnScoreText = "PREVIOUS SCORE: " + score;
}
}
I'm trying to do a 2D platformer with a deaths counter, but I'm facing a problem.
Here is the script I attached to a 3D Text:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class DeathCounter : Respawn
{
public Text DeathCount;
public void SetText(int text)
{
string deathsS = deaths.ToString();
DeathCount.text = deathsS;
}
}
And it does nothing.
I'm asking for help please.
What do I do ?
Here is the "Respawn" script, if needed:
using System.Collections;
using UnityEngine.SceneManagement;
using System.Collections.Generic;
using UnityEngine;
public class Respawn : MonoBehaviour
{
public int deaths;
private Scene scene;
void Start()
{
scene = SceneManager.GetActiveScene();
}
void OnCollisionEnter2D(Collision2D col)
{
if(col.transform.CompareTag("Player"))
{
deaths = deaths + 1;
Debug.Log("You are dead");
System.Threading.Thread.Sleep(500);
SceneManager.LoadScene(0);
}
}
}
Thanks a lot for your help !
Have a nice day.
First thing I would do is remove the inheriting of Respawn from your DeathCounter class since it's not extending the functionality of Respawn. I would then set the function to use the passed in parameter to set the text value instead.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class DeathCounter : MonoBehaviour
{
public Text DeathCount;
public void SetText(String text)
{
DeathCount.text = "Death Count: " + text;
}
}
Then, in your other class when doing your collision check you can pass the death count value as what to set your DeathCount text to.
using System.Collections;
using UnityEngine.SceneManagement;
using System.Collections.Generic;
using UnityEngine;
public class Respawn : MonoBehaviour
{
public int deaths;
//Reference to your DeathCounter script
public DeathCounter dCounter;
private Scene scene;
void Start()
{
scene = SceneManager.GetActiveScene();
}
void OnCollisionEnter2D(Collision2D col)
{
if(col.transform.CompareTag("Player"))
{
deaths = deaths + 1;
//New line here, with passed in script for updating as a reference
dCounter.SetText(deaths.ToString());
Debug.Log("You are dead");
System.Threading.Thread.Sleep(500);
SceneManager.LoadScene(0);
}
}
}
Edit: One script version...
using System.Collections;
using UnityEngine.SceneManagement;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Respawn : MonoBehaviour
{
public int deaths;
//Reference to your Text, dragged in via the inspector
public Text deathCount;
private Scene scene;
void Start()
{
scene = SceneManager.GetActiveScene();
}
void OnCollisionEnter2D(Collision2D col)
{
if(col.transform.CompareTag("Player"))
{
deaths = deaths + 1;
//Just update the referenced UI text
deathCount.text = "Death Count: " + deaths;
Debug.Log("You are dead");
System.Threading.Thread.Sleep(500);
SceneManager.LoadScene(0);
}
}
}
The problem
basically lies in the LoadScene:
You increase the deaths value BUT then you reload the scene → you also reload the Respawn instance and therefore deaths again will have its original value so probably 0.
You didn't even call the SetText method but even if you would when the scene is reloaded also the Text component is reloaded and will have the original text like of you hadn't called the SetText method at all.
Solution
I'm order to fix it in this case I would use
public class Respawn : MonoBehaviour
{
public static int deaths{ get; private set; }
...
if the value is static it is not bound to a certain instance of Respawn but "lives" directly in the type Respawn. Therefore it keeps its current value also when reloading the scene. The {get; private set;} turns it from a field into a property which can be read by every other class but written only by the Respawn class.
Further you never want to use something like
System.Threading.Thread.Sleep(500);
in the Unity main thread. This completely freezes the main thread and if you e.g. later add any animations etc the app will simply freeze completely. Instead use a Coroutine like
void OnCollisionEnter2D(Collision2D col)
{
if(!col.transform.CompareTag("Player")) return;
deaths += 1;
Debug.Log("You are dead");
StartCoroutine(Reload());
}
private IEnumerator Reload()
{
yield return new WaitForSeconds(0.5f);
SceneManager.LoadScene(0);
}
Finally you have to set the Text after loading the scene so it gets updated also in the reloaded scene.
public class DeathCounter : Respawn
{
public Text DeathCount;
// automatically called when scene is reloaded
private void OnEnable()
{
DeathCount.text = deaths.ToString();
}
}
Sidenote:
In Respawn you store a
scene = SceneManager.GetActiveScene();
but later never use it but instead anyway use
SceneManager.LoadScene(0);
so I would get rid of that Start method .. it only causes unnecessary overhead.