I have a TextMeshPro Input Field but my various attempts at getting the Text component are producing null reference exceptions. The Input Field is called Name. I reference this object when the player clicks OK after submitting their name.
Here is the GetName script:
public class GetName : MonoBehaviour
{
GameObject Name;
// These two are left over from previous attempts.
public TextMeshProUGUI player_name;
public TMP_InputField player_inputField;
private string monicker;
// Integer function should be less bother than a bool when called from another script.
public int IsNameEmpty()
{
monicker = Name.GetComponent<TMP_InputField>().text.ToString();
// Program never gets this far.
The OK function in the other script is:
public class WelcomeButtons : MonoBehaviour
{
public GetName getName;
void TaskOnClick6()
{
Debug.Log("You have clicked the OK button!");
int isName = getName.IsNameEmpty(); // Causes null reference exception.
// Program never gets this far.
A simple method to get the text from the input field
public class GetName: MonoBehaviour
{
public TMP_InputField name;
public void TaskOnClick()
{
if(name =="")
{
Debug.log("NO Name Found");
}
else
{
Debug.log("NAME: "+name);
}
}
Yes, if you attached the script above to an empty GameObject, then your script is missing the Link to the GameObject containing the TMP_InputField-Component. You can fix this in two simple ways, just decide what fits best for you:
a) Attach the GetName-script to the same GameObject that also contains the TMP_InputField-Component. Replace the Line "Name.GetComponent<TMP_InputField>().text.ToString();" with "GetComponent<TMP_InputField>().text.ToString();".
b) Leave the GetName-script on the empty GameObject. Make the "GameObject Name;" line public by changing it to "public GameObject Name". Go to the Unity-Editor. When selecting the GameObject containing the GetName-script, you should see the Name-Property and an empty Field next to it in the Inspector. Drag&Drop the GameObject containing the TMP_InputField-Component into it.
So, why did your code not work before? Well, the GetName-script needs some kind of reference to your TMP_InputField-component, which was missing. You tried this by getting it from the Name-Property, but never assigned it any value.
Thanks for all the advice I've received. I now have a working version using the OnClick() approach:
public class GetName : MonoBehaviour
{
public TMP_InputField user_inputField; // User-entered name.
public void SaveName()
{
Globals.player = user_inputField.text.ToString(); // Saved for later.
Debug.Log("Globals.player is " + Globals.player);
}
public void ResetName()
{
user_inputField.text = "";
Globals.player = "";
}
}
just two lines will work :
using UnityEngine.UI;
and
public TMPro.TMP_InputField Name;
Related
So for some reason when I try to access variables from another script the text is showing what I put but when I change things in the editor the accrual values don't change. even if in the code I put playerPoints to 9 when I run it its 0 during a collision. I have all the scripts and objects connected up, and when I try to flip where the variables are, make them in the destroy script, for some reason, it doesn't work. it wont let me use public BallsText bt; it will return an error even though it works fine for the other. I'm sorry if all of these are really basic questions but I've looked as far as I can on the internet and I cant find anything, so any help would be appreciated.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class BallsText : MonoBehaviour
{
public Destroy destroy;
// public int playerPoints = 0;
//public int enemyPoints = 0;
//int playerPoints = 0;
//int enemyPoints = 0;
public Text playerPointsText;
public Text enemyPointsText;
void Update()
{
playerPointsText.text = destroy.playerPoints.ToString();
enemyPointsText.text = destroy.enemyPoints.ToString();
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Destroy : MonoBehaviour
{
public int playerPoints = 9;
public int enemyPoints = 0;
void OnCollisionEnter2D(Collision2D col)
{
if (this.name == "Destroy.cs")
{
Debug.Log("i have a doughnut");
}
if (col.gameObject.name == "Player")
{
Debug.Log(playerPoints);
playerPoints++;
Debug.Log(playerPoints);
Debug.Log("at least im a dounut");
Destroy(this.gameObject);
}
else if (col.gameObject.name == "Enemy Zone")
{
enemyPoints++;
Destroy(this.gameObject);
}
}
}
You can hide them from the inspector's view with HideInInspector while still being public.
[HideInInspector] public int playerPoints = 9;
You can also use the reset script to return the setting of public numbers to the first state.
Like Everts said, you had the value at zero and created an instance of the script. Now when you change the script Unity will serialize (save) all the public/serialized fields, then reload the script, then deserialize (load) all the public/serialized fields it previously saved. This means your values are locked into the instance that you previously used. If you make a new instance, you get the current values, and if you reset the script you'll get the current values.
This has also bitten me enough times that I don't set default values when variables are declared anymore. If you have a value that you want to use, set those values in Start() or Awake().
The advantage here is that whatever is in the script will get overwritten when play mode starts. The disadvantage is that you can't customize those values on a per-instance basis anymore, because all instances of the script will all load the same default values when play mode starts. If this matters to you, and you want to be able to customize those values, then unfortunately you'll need to go to each script and change those values manually.
If you use a property with an automatic backing field then you won't be able to see it in the editor
public class Destroy : MonoBehaviour
{
public int playerPoints{get; set;} = 9; // Can't see this in the editor
public int enemyPoints{get; set;} = 0; // Can't see this in the editor
If you use a property with an explicit backing field then you can expose the backing field to the editor with the [SerializeField] tag, but then you've got the same problem you've got now - the editor will serialize that field and subsequent changes to the script won't affect instances:
public class Destroy : MonoBehaviour
{
[SerializeField]
private int playerPoints_ = 9; // Instances will "lock in" values and later changes to the script here won't take effect
[SerializeField]
private int enemyPoints_ = 0; // Instances will "lock in" values and later changes to the script here won't take effect
public int playerPoints
{
get=>playerPoints_;
set{playerPoints_ = value;}
}
public int enemyPoints
{
get=>enemyPoints_;
set{enemyPoints_ = value;}
}
If you keep the fields public (and thus exposed to the editor) but set the values at runtime in Awake() or Start() then you can see the values in the editor but the editor values for all instances will be overridden when play mode starts:
public class Destroy : MonoBehaviour
{
public int playerPoints; // Doesn't matter what the value is here on instantiation becuase you'll override it on Awake()
public int enemyPoints; // Doesn't matter what the value is here on instantiation becuase you'll override it on Awake()
public void Awake()
{
playerPoints = 9; // Will override *every* instance's values with this.
enemyPoints = 0; // Will override *every* instance's values with this.
}
:EDIT:
I'll add too that repeatedly polling is wasteful. If you use events then you can subscribe and get notifications when there's something to see. Consider instead:
public class Destroy : MonoBehaviour
{
private int playerPoints = 9;
private int enemyPoints = 0;
public System.EventHandler<int> OnPlayerPointsChanged;
public System.EventHandler<int> OnEnemyPointsChanged;
void OnCollisionEnter2D(Collision2D col)
{
if (this.name == "Destroy.cs")
{
Debug.Log("i have a doughnut");
}
if (col.gameObject.name == "Player")
{
Debug.Log(playerPoints);
playerPoints++;
OnPlayerPointsChanged?.Invoke(this, playerPoints);
Debug.Log(playerPoints);
Debug.Log("at least im a dounut");
Destroy(this.gameObject);
}
else if (col.gameObject.name == "Enemy Zone")
{
enemyPoints++;
OnEnemyPointsChanged?.Invoke(this, enemyPoints);
Destroy(this.gameObject);
}
}
}
Now Destroy has two public events that fire when the public or enemy points change. Anyone subscribing to those events will get notified when the points change, and part of the event notification is the current point value.
Then your other script subscribes to the events and does whatever they need to when they receive that event. Here they'll convert the points .ToString() and update the Text values:
public class BallsText : MonoBehaviour
{
public Destroy destroy;
public Text playerPointsText;
public Text enemyPointsText;
private void Start()
{
destroy.OnPlayerPointsChanged += PlayerPointsChanged;
destroy.OnEnemyPointsChanged += EnemyPointsChanged;
}
public void PlayerPointsChanged(object sender, int points)
{
playerPointsText.text = points.ToString;
}
public void EnemyPointsChanged(object sender, int points)
{
enemyPointsText.text = points.ToString();
}
}
Last note here is that your Destroy script increments enemyPoints but then also immediately destroys the gameObject, so I don't see the point in incrementing enemyPoints unless there's something else accumulating enemy points. With the subscription model that's totally doable - you could have something else subscribing to the Destroy script models and they'll get notifications before the script self-destructs.
Ok. When I open my project if someone GameObject (or all of them) is not found then it should be created automatically.
For example, I have SceneBuilder as GameObject and it contains all necessary scripts: ObjectsGenerator.cs, PlayerData.cs etc. And if somehow SceneBuilder disappears it must be recover from code. How can I do that?
p.s.: I found only two ways:
creating it from menu [MenuItem("MyTools/CreateGameObjects")], but it not obviously for support.
creating it through [CustomEditor(typeof(SomeOjectScript))] , but it must be already exists in scene (I guess a situation where the scene is completely empty)
p.s.: sorry if the question has already been and I have not found it
updated.
thanks to all. solved something like that
[InitializeOnLoad]
public class Checker : MonoBehaviour
{
static Checker()
{
CheckAndCreateObj();
EditorApplication.hierarchyChanged += hierarchyChanged;
}
private static void hierarchyChanged()
{
CheckAndCreateObj();
}
private static void CheckAndCreateObj()
{
string objName = "OBJECTNAME";
GameObject go = GameObject.Find(objName);
if (go == null)
{
Instantiate(new GameObject(objName));
}
}
}
https://docs.unity3d.com/Manual/RunningEditorCodeOnLaunch.html
You can check it dynamicly in one of your other scripts. You can call it from OnEnable or Start functions. Your MonoBehaviour must contain tag [ExecuteInEditMode] It could be something like this:
[ExecuteInEditMode]
public class YourMonoBehaviour: MonoBehaviour
{
void OnEnable()
{
var myObject = GameObject.Find("GAMEOBJECT_NAME");
if (myObject == null)
{
//Create new GameObject here and add your Component
}
}
}
If you need save some asset links or data in your gameobject with serialization, you can create prefab, cache it and create your object from prefab:
MyObjectType prefabTemplate;
void OnEnable()
{
if (GameObject.Find("GAMEOBJECT_NAME") == null)
{
GameObject.Instantiate(prefabTemplate);
}
}
This problem is on Unity, but I think I am just doing the c# side wrong.
EDIT
It looks like by doing the code and storing a child Class B of parent Class A, and by saying is a Class A type, by modifing my variable of type Class A containing Class B I modify some sort of hybrid Class A/B that doesn't represent my real Class B Script
What I do is, having multiple script on different prefabs. Each of those script represent an item and all have has parent Usable which is a class that I actually use like an interface, but that will in the future get some stuff.
The full WeaponLaser and Usable script is below
When the player go over a drop, I instantiate the gameObject containing the script like this (using prefab)
GameObject Item = Instantiate(droppedItem, transform.position, Quaternion.identity);
Item.transform.parent = transform;
usableItem = droppedItem.GetComponent<Usable>();
usableItem.OnUsed += ReleaseItem;
and Use the item like this
if (usableItem != null)
usableItem.Use(firePoint.position);
The thing is, it looks like the script I call when I do Use() is another version.
I mean, If I set int fireCurrentShoot = 10; on top of the script WeaponLaser and then trought code in the Start for exemple I do fireCurrentShoot = 2;
It will work on the inside the script WeaponLaser, but when I call it using the above code
if (usableItem != null)
usableItem.Use(firePoint.position);
It will show fireCurrentShoot = 10 so without the modification
END EDIT
Hello,
I have a problem with heritage I don't understand, I cleaned all my class, and still I can't find why.
I have a class A :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Usable : MonoBehaviour
{
protected virtual void Start()
{
}
protected virtual void Update()
{
}
public virtual void Use(Vector3 pos)
{
}
protected virtual void Used()
{
}
}
and a class B
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WeaponLaser : Usable
{
const int SHOOT_AVAILABLE = 5;
const float FIRE_COOLDOWN = 1;
float fireCurrentCooldown = 0.0f;
int fireCurrentShoot = 0;
protected override void Start()
{
base.Start();
Debug.Log("start");
fireCurrentShoot = SHOOT_AVAILABLE;
Debug.Log("fireCurrentShoot" + fireCurrentShoot);
}
protected override void Update()
{
Debug.Log(fireCurrentShoot); // value is = 5
base.Update();
}
public override void Use(Vector3 shootPosition)
{
Debug.Log(fireCurrentShoot);// value is = 0
base.Use(shootPosition);
base.Used();
}
void FireCooldown()
{
}
}
when I call Use, my Debug.Log of booth value give 0... but I am expecting to have fireCurrentShoot = 5
I call it like this *:
usableItem = droppedItem.GetComponent<Usable>();
usableItem.Use(firePoint.position);
why he is equal to 0?
Your inheritance seems fine, so the problem might be in the way you use these objects in Unity. This makes it a bit more difficult to be sure what the solution is, but here is what you could try:
Be sure that Start is called: I think it is the case for you since
you say you see a debug print, but remember that Start is not called
on objects that are deactivated in the Editor, so some values that
you set in start will not be initialized.
Name your objects in unity editor with unique names and add the name in the debug
Debug.Log(fireCurrentShoot + ", " + name);
This should help you sure you see the values for the object that interests you
You can also replace
int fireCurrentShoot = 0;
by a property:
int _fireCurrentShoot = 0;
private int fireCurrentShoot
{
get{ return _fireCurrentShoot;}
set{ _fireCurrentShoot = value; Debug.log("fireCurrentShot set to " + _fireCurrentShoot);}
}
this will allow you to see a message when the value is modified. You
can also set a debug point on the setter to see the callstack
EDIT:
I think I got it: you don't register to the instanciated object, but to your prefab
replace
usableItem = droppedItem.GetComponent<Usable>();
by
usableItem = Item.GetComponent<Usable>();
I'm not really sure how to describe it exactly so let me show you what is going on.
I have a PlayerControls script which looks like this (note: I stripped everything except for the necessities).
namespace Player.Controls {
internal class PlayerControls: MonoBehaviour {
public bool IsClimbing { get; private set; } = false;
public bool IsGrounded { get; private set; } = false;
}
}
These variables are set in this class depending if the player is climbing/touching the ground. This script resides on the "Player" GameObject in the scene.
I have another script called PlayerControllerwhich looks like this
using Player.Controls;
public class PlayerController: Singleton<PlayerController> {
internal PlayerStats stats = new PlayerStats();
//PlayerStats nested class (see below)
}
The Singleton class only checks if the generic type is null, if it is, it will use FindObjectOfType to get an instance. This script also resides on the "Player" GameObject.
Inside the PlayerController class, I have a nested class called PlayerStats. It looks like this
internal class PlayerStats : PlayerControls {
public new bool IsClimbing { get { return base.IsClimbing; } }
public new bool IsGrounded { get { return base.IsGrounded; } }
}
Notice this nested class in inheriting from PlayerControls.
The idea is that the PlayerControls class in inaccessible to all other classes except for PlayerController, and any information I want to obtain regarding the player can be obtained by getting the player's instance (via the singleton) and accessing the PlayerStats variable.
For example, assuming the variable inside Singleton which holds the instance is called Instance, one could do PlayerController.Instance.stats.IsClimbing; Everything works as expected, except for one thing.
In the Awake method of the PlayerController class, I do this
private void Awake() {
Debug.LogFormat("In PlayerController Awake(). Is PlayerController.stats null? {0}",
(stats.Equals(null) ? "Yes" : "No"));
Debug.LogFormat("IsClimbing : {0}", stats.IsClimbing);
}
In the output window, it prints
In PlayerController Awake(). Is PlayerController.stats null? Yes
IsClimbing : False
If I also put the same IsClimbing debug in the Update() method, the value is correct for when I start climbing.
So, finally, my question, how can I access the variables of the PlayerStats class with the stats variable if stats is null? I thought it may have been somehow calling straight to the PlayerControls properties, so I changed their names, removed the new inside of PlayerStats and even put a debug statement inside one of the properties inside PlayerStats, and it definitely gets called. For example,public bool IsClimbing { get { Debug.Log("Called IsClimbing inside PlayerStats."); return base.Climbing; } }
If it is getting called and working properly, how can it be null? I asked my professor and he doesn't seem to know why either. What is really going on here?
Edit:
As requested, the Singleton class:
public abstract class Singleton<T>: MonoBehaviour where T : MonoBehaviour {
private static T instance;
public static T Instance {
get {
if(instance == null) {
instance = FindObjectOfType<T>();
}
return instance;
}
}
}
Here is an image of the console output.
Digging around on the Unity forums it appears that the Equals method has been overridden (on Object which MonoBehaviour eventually derives from) which is why comparing a MonoBehaviour to null is not giving you what you might expect. The answer I link to suggests code like this is more appropriate:
stats == null || stats.Equals(null)
I'm making a game in Unity3D with C# for mobile devices and can't figure out how to check which scene was loaded before the current scene. I need to check this to change the spawn point from the player gameobject. First I added a simple script to my buttons (loadnextscene and loadprevscene)
public class SwitchScene : MonoBehaviour {
public int sceneNumber;
public void LoadScene(int sceneNumber) {
Application.LoadLevel(sceneNumber);
}
}
A second scripts handles the touch input from the user and changes the movement of the player object.
So, for example: If the player clicks on the "load previous scene" button in the second Level to switch to the first level again, I want to set the spawn point of the player object on the right half on the screen and not on the left side like when the game was started the first time.
I tried it with Singleton and PlayerPrefs, but it did not work out.
You need to save the scene number to some variable before LoadScene, then check it after the scene loaded.
The only problem is that this variable will be destroyed after the new scene is loaded. So, to prevent it, you can use DontDestroyOnLoad. Here is what you do:
First, create a new empty game object, and attach the following script to it:
using UnityEngine;
using System.Collections;
public class Indestructable : MonoBehaviour {
public static Indestructable instance = null;
// For sake of example, assume -1 indicates first scene
public int prevScene = -1;
void Awake() {
// If we don't have an instance set - set it now
if(!instance )
instance = this;
// Otherwise, its a double, we dont need it - destroy
else {
Destroy(this.gameObject) ;
return;
}
DontDestroyOnLoad(this.gameObject) ;
}
}
And now, before you load, save the scene number in the Indestructable object:
public class SwitchScene : MonoBehaviour {
public int sceneNumber;
public void LoadScene(int sceneNumber) {
Indestructable.instance.prevScene = Application.loadedLevel;
Application.LoadLevel(sceneNumber);
}
}
And last, in your scene Start() check Indestructable.instance.prevScene and do your magic accordingly.
More info here:
http://docs.unity3d.com/ScriptReference/Object.DontDestroyOnLoad.html
*I did not compile the code, so there may be some errors, but this is the general idea.
Why did the PlayerPrefs approach did not work?
I think its the easiest way to solve your problem.
public class FirstLevel : MonoBehaviour {
public void Start() {
PlayerPrefs.SetString("SceneNumber", SceneManager.GetActiveScene().name);
}
}
And then in the second scene simply read the saved PlayerPrefs
public class SecondLevel : MonoBehaviour {
string PrevScene;
public void Start() {
PrevScene = PlayerPrefs.GetString("SceneNumber");
// if there will be a third scene, etc.
PlayerPrefs.SetString("SceneNumber", SceneManager.GetActiveScene().name);
}
public void GoToPrevScene() {
SceneManager.LoadScene(PrevScene);
}
}
You can solve this problem with a single static member variable in the SwitchScene class. No need for the singleton pattern or DontDestroyOnLoad.
public class SwitchScene : MonoBehaviour
{
public int sceneNumber;
private static int previousScene;
private int oldPreviousScene;
void Start()
{
oldPreviousScene = previousScene;
previousScene = sceneNumber;
}
public void HandleLoadPrevButtonClick()
{
SceneManager.LoadScene(oldPreviousScene);
}
}