Script is getting the prefab's default data? - c#

Using Unity. When instantiating a new gameObjectA with ScriptA, I access gameObjectB's script (ScriptB) while doing so. The thing is, I can only use the prefab of gameObjectB, instead of an already instantiated gameObjectB. For example -
public class ScriptA : MonoBehaviour
{
public ScriptB scrptB;
void Start()
{
float X = scrptB.IntegerThatIncreasesEveryFrame;
Debug.Log(X);
}
In every frame that a gameObjectB has been instantiated, I have integer IntegerThatIncreasesEveryFrame (I'll call it ITIEF now) that, of course, adds 1 to itself every frame.
When gameObjectA and ScriptA gets instantiated, I want to use gameObjectB's ScriptB's ITIEF (e.g. 100 after 100 frames after being instantiated).
I have to use a prefab of gameObjectB, though. When gameObjectA gets instantiated, it uses the DEFAULT value of ITIEF (which is zero).
If this makes sense please help!

In order to access the information from an instanced object, you need to have a reference to the object. In your case, you are referencing the prefab, not the instanced one in game.
The reason why you cannot drag the script itself into the Inspector is because a reference needs to reference a script that is instanced. ScriptA cannot make a reference to ScriptB unless ScriptB is instanced into a gameobject. A prefab acts as an instanced object that doesn't 'exist' in the game. If you want a reference to the instanced script that IS in the game, then you need to set the ScriptB variable to be that in-game script.
ScriptB instance = GameObject.FindObjectObType<ScriptB>();
This line finds the first instanced form of ScriptB.
This only works if there is ONE instance of ScriptB in the game. If there are more, you need to do a more complicated bit of code.
Are there the same amount of ScriptA and ScriptB? Then when they are both first instanced, store ScriptB somewhere, and then update ScriptA with it.
Your question leaves a lot more questions, but I think I've covered most of the solutions. Pretty much, if you have more than one ScriptB instance, then store the one you want to use and then set it on the ScriptA instance. Otherwise, use FindObjectOfType to get a single instance.

Related

(Unity) gameObject not referencing the prefab

EDIT: I found the problem. I am simply a big dumb!
I removed "public static" from "IEnumerator" and that fixed everything.
I am a COMPLETE beginner to programming, and this is the first program I have ever written. To learn, I decided to remake simple games and Flappy Bird was my first attempt.
my problem: I declared a gameobject with "[SerializeField] GameObject" but cannot assign the prefab in the inspector.
This script "SpawnPipes" is attached to an empty gameobject "PipeSpawner"
[SerializeField] public GameObject Pipe;
//declared some other variables and stuff..
void Start()
{
StartCoroutine(SpawnRoutine());
}
public static IEnumerator SpawnRoutine()
{
while (spawningOn == true)
{
Instantiate(Pipe,
new Vector3(xpos, UnityEngine.Random.Range(minYPos, maxYPos), 0), Quaternion.identity);
Debug.Log("spawned a pipe");
yield return new WaitForSeconds(spawnRate);
}
}
As you can see, I have declared a gameobject "Pipe" which is serialized, and so should show up in the inspector. And it did... at first.
I was doing some final bug fixing, when suddenly this code broke. I was editing a different script (which includes code that turns "spawningOn" to true or false depending on the gamestate) and when I tested it, a new error showed up my SpawnPipes script, which I hadn't even touched!
Here is the error message:
An object reference is required for the non-static field, method, or property 'SpawnPipes.Pipe'
The error refers me to the line
Instantiate (Pipe,
When I hover over "Pipe," is highlights my "GameObject Pipe;" so I know it's properly relating the two. But it seems to think I do not have a prefab assigned, which I did.
The first thing I tried was changing my "GameObject Pipe" to "static GameObject Pipe". This removed the error, but when I tested the game the pipes did not spawn and I got the message that the object I was trying to instantiate was null, as in there was no object assigned to "GameObject Pipe" ..
I opened my gameobject "PipeSpawner" in the inspector, and looked at the "SpawnPipes" script, and for some reason, my Pipe field was gone. Remember, I declared my GameObject Pipe as both serialized AND public, so there is no reason it should be missing!
I erased the GameObject declaration code and rewrote it with and without "static", the field did not return. I reimported all of my prefabs, the field did not return. I relaunched Visual Studios and Unity, the field did not return.
Eventually, I do not remember what did it, but I did get the field to come back and I assigned my pipe prefab to it once again.
However, it STILL says
An object reference is required for the non-static field, method, or property 'SpawnPipes.Pipe'
The prefab is assigned!! Only one field is named "Pipe" so I know it is assigned to the right field!! Please help!
[SerializeField] public GameObject Pipe;
is non-static field that means that static functions can't access it, consider to turn the function to non-static
public IEnumerator SpawnRoutine()
in this case you won't be able to call the function directly you need an instance of the class, you could use singleton to call the function.
It sounds like you needed to add the script component to the gameObject and then, in the inspector with the gameObject selected, scroll down to the script and drag the gameObject into the empty bar where it goes. I could be wrong, because I'm not looking at your interface on Unity, but it sounds like maybe you need to do that.

Singleton not getting Referenced in Start() Function after Scene Change

I am trying to understand why I am getting a missing reference exception after a scene change when using singletons.
So, I have two GameObjects in the first scene. A main camera with a GameManager script attached and another shop object with a Purchaser script attached. Both scripts are also singletons created like this for example:
public static Purchaser Instance
void Awake(){
Instance = this
}
They then both reference each other in the Start() function, again like this for example:
void Start(){
game = GameManager.Instance
}
Before a scene change, both scripts use each others singleton references to call methods from one another and everything seems to be working fine. Once I change scenes neither of those objects is in the next scene so they both get destroyed. However, once I go back to the main scene I receive a missing reference exception when the purchaser script attempts to call a method from GameManager using the singleton reference it gets from Start(), that changes the text of a text object attached to the main camera. This function is called after a button is pressed that is attached to the shop object that calls this function in the Purchaser script:
UpdateMoney(){
game.UpdateMoney(100);
}
I read around and was seeing that this may be because Start() will only being called once throughout the whole game run. Which meant the GameManger singleton instance of the Purchaser script was still the old one from before the scenes changed and that instance was destroyed. But, I just tested to see if this was true by putting a debug log in each scripts Start() function, and saw that after each scene change back to the main scene the debug log would go through from both scripts Start(). So, would it be right to say Start() is only ever called once for the life of script but not for the whole game run right? Shouldn't this also mean that once the game changes back to the main scene and both GameObjects are created again, the Purchaser script should now have an updated reference to the newly created GameManager script since Start() was called again?
What I also found was that this worked instead of using the game reference in UpdateMoney():
UpdateMoney(){
GameManager.Instance.UpdateMoney(100);
}
So, why does this work instead of using the game reference retrieved in Start()? Does this mean when Start() is called GameManager.Instance is still the old GameManager.Instance which is why game = GameManager.Instance does not work? Sorry this is very wordy. Any help is much appreciated.
Based on what I understand, you have a singleton made in the main scene. The game object where the singleton is attached to is destroyed after switching scenes.
First of all, is there a reason why you need the singleton to be a MonoBehaviour and attached to a game object? Because you can just make a class with
private static MyClass instance = null
public static MyClass Instance
{
get {
if(instance == null)
instance = new MyClass();
return instance;
}
}
This way, your singleton will always have a value and can be passed thru scenes unless instance is set to null.
Second, Start() is called once only. However, I encountered issues before that if the MonoBehaviour is attached to a disabled game object, the Start() will not be called. You can check if this is what's happening to you when you switch back to the main scene.
Third, if you really need the singleton to be a MonoBehaviour, you can use DontDestroyOnLoad(instance.gameObject) so the game object of the singleton will not be destroyed even after a scene switch. However. I assume the game objects are set on the scene. If it is not from a prefab, you can just do something like this
private static MyClass instance = null;
public static MyClass Instance
{
get {
if(instance == null){
GameObject inst = new GameObject("MyClass Singleton");
instance = inst.AddComponent<MyClass>();
DontDestroyOnLoad(inst);
}
return instance;
}
}
If you do this, then you can remove the preset game objects from the main scene and let the first call to MyClass.Instance make the game object for you.
Lastly, if you do not want to do that, you should set instance = null; on the game object's OnDestroy() so that when you enter the main scene, the new instances will be set to the singleton. This would mean the singleton will not have a value after you switch scenes outside of the main scene.

What is better: link to a GameObject or to a Class in Unity?

Example: GameObject A has a script attached to it called MakeItRain. Inside that script is a public void Drizzle();
GameObject B also has a script and wants to tell MakeItRain to do Drizzle();
Inside the script of GameObject B, I can do this:
public GameObject makeitrain;
and then I have to use GetComponent to reach Drizzle(); in my code.
In the inspector, I drop GameObject A into the slot of makeitrain and I'm done.
However, I could also do this in the script of GameObject B:
public MakeItRain makeitrain;
and then just call makeitrain.Drizzle(); in my code of GameObject B's script, without GetComponent.
In both cases, in the Inspector, I have to drag and drop GameObject A into the slot of GameObject B.
Is there a difference or reason why I should definitely not do the last option? I understand that the first method gives me more flexibility because I could call other components of GameObject A as well and not just the script's stuff. Just wondering if there is any other rationale for not doing the second method.
The answer depends if you need to call any function or use variable from the MakeItRain script.
If you don't need to to call any function in the MakeItRain script or access any variables from it then it is better to use GameObject as the reference. Also, if what you need to do is activate, de-active, rotate the GameObject then use the GameObject as reference.
On the other hand, if you need to be able to call a function such as Drizzle or access a variable from the MakeItRain script from multiple places, then you need to use the MakeItRain reference. At this time, it doesn't make sense to use the GameObject reference since by using it, it's required to use GetComponent every-time you need to call a function or access a variable from the MakeItRain script attached to it.
Finally, when using the MakeItRain script to reference your object, you can directly and easily access the GameObject it is attached to without using the makeitrain.gameObject. This doesn't require the use of the GetComponent function.
Just wondering if there is any other rationale for not doing the
second method.
Performance issue due to the required use of the GetComponent function is the reason. Using it once in the Start or Awake function and initializing your MakeItRain variable is better.
For example, this is better:
public MakeItRain makeitrain;
void Start()
{
makeitrain = GetComponent<MakeItRain>();
}
void Update()
{
makeitrain.Drizzle();
}
than this:
public GameObject makeitrain;
void Update()
{
makeitrain.GetComponent<MakeItRain>().Drizzle();
}
And should be used to avoid having to search for the component on the native side every frame.
Using MakeItRain and explicitly defining the type is better than using GameObject.
As #hacksalot commented, using MakeItRain offers strong typing. One of the benefits of this is related to your comment:
In both cases, in the Inspector, I have to drag and drop GameObject A
into the slot of GameObject B.
If you explicitly set the public variable type to MakeItRain rather than GameObject, it is not possible to drag and drop a GameObject A into the slot of GameObject B unless GameObject A is has a script of the correct type. This gives you a compile-time/editor-time check that you are linking to the correct GameObject in the Unity Editor inspector.
Also, while not necessarily so, using GameObject references often encourages messier code, whether because of unnecessary chaining of methods together (e.g. GetComponent) just because the type wasn't specified, or because it adds a bit of friction to writing & using helper methods. Consider even in a simple example which one reads better:
makeitrain.Drizzle()
makeitrain.GetComponent<MakeItRain>().Drizzle()
I understand that the first method gives me more flexibility because I
could call other components of GameObject A as well and not just the
script's stuff.
Note that you still have the flexibility to access GameObject, it's just a bit more verbose (which is one downside of this approach):
public MakeItRain makeitrain;
void Start()
{
makeitrain.gameObject.SetActive(false)
}
However, you'll likely be using helper methods anyway (for anything more than basic calls), or even wrapper methods (which are inconvenient to write but sometimes helpful for readability).
In most cases, the benefits of linking to the class rather than the GameObject outweigh the downsides.
If u don't want to use GetComponent(), you can simply use SendMeassage(), like this
public Gameobject makeItRain;
void Start(){
makeitrain.SendMeassage("Drizzle");
}
Another way to link a script is that use FindObjectOfType(), which do not need to drag and drop GameObject into the slot, here is the sample
void Start(){
MakeItRain makeitrain = FindObjectOfType("MakeItRain");
}
Also you can use Gameobject.Find() to link a GameObject instead of dragging into slot, but I don't recommand this way, it cost a lot performance since you need to find every single GameObject in scene.

Unity create a permanent access to an gameobject

in many of my scripts I have to deal with the playerObject. I don't want to search for it in each script.
I thought about creating an static class to have permanent access to the player.
This is how I do it
public class Globals
{
public static GameObject playerObject = GameObject.FindGameObjectWithTag(StringCollection.PLAYER); // The playerObject
public static Rigidbody playerRigid = GameObject.FindGameObjectWithTag(StringCollection.PLAYER).GetComponent<Rigidbody>();
} // The rigidbody of the player
So I want to store all the variables that I need many times. When loading a new scene, it says the rigidbody tries to access an object, that got destroyed previously.
How can I fix it?
I thought about creating an object inheriting from Monobehaviour that gets never destroyed but then I would have to create a reference to it in all my scripts..
Anything that inherits from Unity's UnityEngine.Object are destroyed even when made static.
If you want it to stay, you have to call DontDestroyOnLoad on it. In this case call:
DontDestroyOnLoad(Globals.playerObject);
This should make the playerObject and every component that is attached to it to stay through the next scene.

Unity MissingReferenceException when loading same scene for second time

I'm trying to create Arkanoid 3d game using Unity with C#. I've created simple Menu (Scene 0), where I can start my game, my main scene where actual game takes place(Scene 1) and Scoreboard (Scene 2), which is shown after losing all 3 balls Player has at start. After pressing any key i go back to Menu and can start game again. And this is where problem begins.
During second game after loosing 1st ball, my game goes crazy. I get loads of "MissingReferenceException"s like one below (but some linked to other objects (like GUIText's etc):
MissingReferenceException: The object of type 'Player' has been destroyed but
you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.
Player.BallLost () (at Assets/Player/Player.cs:164)
GameEventManager.TriggerBallLost () (at Assets/Menagers/GameEventManager.cs:30)
Ball.Update () (at Assets/Ball/Ball.cs:47)
I noticed loads of MissingReferenceExceptions that are casued by not assigning variables. But this feels kinda diffrent for me since it all works perfectly during "1st play". What can cause this problem? I cheked in inspector after launching game for the second game and all variables are assigned to objects.
I'm not sure if shoudl insert game code since it has grown rather big and is split into >10 scripts.
In my case, the problem were two static events. One assigned to call a method whenever it was raised (created by another class), and one created in this class to inform other classes for the occurance of something.
So I just added the following two in the OnDestroy() method:
OtherClass.onNewX_event -= X_eventHandler;
for the fist one (where OtherClass was the other class which was raising the onNewX_event and the current class was handlening it)
onThisClassEvent = null;
for the event created and raised in this class.
I'm guessing you used Application.loadLevel(xx). This is what I found out about it:
Reloading the scene should reset all the variables unless you are using static variables because logically creating a new instance of every object would reset its values to their initial state.
Static variables on the other hand are not destroyed because they are part of a class, not an instance. You have to reset these manually.
DontDestroyOnLoad() is a little different. It tells Unity not to destroy an object when you load a new scene. So these objects won't be reset either because they aren't being destroyed and recreated.
The only way to reset them is just to manually go through and turn the variables back to some initial state. It is your choice how you do that. You can either save all the initial values, or copy the values over from a newly instantiated class.
As an addition I'd like to say if you use static variables, it might be more useful to put them all in a Singleton or change them into non-static variables.
Include below function in your GameEventManager class
public static void Nullify(){
GameStart = null;
GameOver = null;
LevelWon = null;
GamePause = null;
GameResume = null;
BallLost = null;
}
and call this function (GameEventManager.Nullify();) in Menu(scene0) before loading other scenes ;
GameEventManager.Nullify();
Application.LoadLevel("Scene1);
Hope this help...... :-)

Categories

Resources