How do I check variables of another object, and call its methods? - c#

I am working with triggers in Unity3d. I have a reference to another object that enters the trigger, and if that object has a variable that is set to a value, then I want to call its own method.
Something along the lines of:
if(gameObject.hasAttribute('canDance') == true)
{
gameObject.dance();
}
What is the correct code to perform such an action?

You need a reference to the script attached to the GameObject. I will assume you already found the GameObject and hold a reference to it named myGameObject. If you don't know how to find it, you can do this:
GameObject myGameObject = GameObject.Find("Name Of GameObject in Scene");
Now to grab the script you can do this:
ScriptName script = myGameObject.GetComponent<ScriptName>();
Then you can make calls to the functions inside the ScriptName class like this:
script.functionName();

Related

Unity c# how to properly use GetComponent [duplicate]

This question already has answers here:
How to access a variable from another script in another gameobject through GetComponent?
(3 answers)
Closed 25 days ago.
I am in no way a coding novice but I just picked up unity and I'm trying "get it"
My question involves this code. it is not part of an active project. it is simply an attempt to understand the system
private SpriteRenderer beans2;
public Sprite beanimmage;
// Start is called before the first frame update
void Start()
{
beans2 = gameObject.GetComponent<SpriteRenderer>();
beans2.sprite = beanimmage;
}
from what I understand this code allows me to manipulate the SpriteRenderer of the game object this code is attached to by assigning it to beans2 and then changing the Sprite of beans2 to beanimmage and works fine in the system
My question is -
Is their a way to assign beans2 to the spriterenderer of a diffident game object? The object i have this code attached to is just a game object called test is their a way to assign the beans2 variable to the SpriteRenderer of my test2 object instead ?
something like this ?
beans2 = test2.gameObject.GetComponent<SpriteRenderer>();
beans2 = gameObject.test2.GetComponent<SpriteRenderer>();
The GetComponent() Function is apart of every gameobject so to get components on other gameobjects you need to reference that gameobject in a variable, something like this:
public GameObject test2;
private SpriteRenderer test2Renderer;
public Sprite beanImage;
void Awake()
{
test2Renderer = test2.GetComponent<SpriteRenderer>();
test2Renderer.sprite = beanImage;
}
Alternatively, you can just get the component of the test2 object from the inspector because you are using public variables. To do this make a sprite renderer variable field and make it public so it is shown in the inspector, then drag the test2 object into the field and it will automatically get the sprite renderer component off that object.
Finally, you could also just do this with one field. This method would be used when you only want to access the GameObject and nothing else:
public GameObject test2;
public Sprite beansImage;
void Awake
{
test2.GetComponent<SpriteRenderer>().sprite = beansImage;
}
Any way works and you should do it the second way if you want to work with public variables and rely on the inspector but if you only need the gameobject then go with method 3. Method 1 is probably the best way to do it so you can change the sprite later if you want.

Calling function from another script attatched to another object not working

I am trying to call a function in another c# script, that is attatched to another gameObject in my scene. I am creating an instance of the script LevelChanger in the Grounded one like this:
LevelChanger levelChanger;
Then, in the Awake() function:
GameObject gameObject = new GameObject("LevelChanger");
levelChanger = gameObject.AddComponent<LevelChanger>();
then calling like this in an IEnumerator:
levelChanger.FadeOut(true); // line 178
Then this is the LevelChanger class:
using System.Collections;
using UnityEngine;
public class LevelChanger : MonoBehaviour
{
public Animator animator;
public void FadeOut(bool fadeIn)
{
animator.SetBool("Fade", true); // line 10
if (fadeIn) StartCoroutine(FadeInAsWell());
}
IEnumerator FadeInAsWell()
{
yield return new WaitForSeconds(0.9f);
animator.SetBool("Fade", false);
}
}
I've been trying to solve this problem for quite a while now, I checked everywhere (here, here, here, here and on other sites). I saw that because my LevelChanger script is attatched to a gameobject, so it is MonoBehaviour, it is not possible to create an instance of the class from another script, like this:
LevelChanger levelChanger = new LevelChanger();
or either "normally" like I was doing at first like this:
LevelChanger levelChanger;
Then just calling its functions like
levelChanger.FadeOut(true);
Most of the time I was getting a NullReferenceException at runtime (at line 176), now directly in the LevelChanger script (at line 10).
At this moment, I truly have no idea how to fix this: does anybody know how?
(I'm still a beginner).
Thanks in advance!
Based on the conversation we've had in comments, I'll write an answer here to be more thorough.
So, let's recap. What you want to do is call a function from LevelChanger. But, you are unsure of how to get a reference to LevelChanger. This is the core problem every programmer faces: How do i get that stuff over there, and what's the best way to do?
Based on what you've said, your prefab exists in the scene, so you want to grab a reference to it.
One simple, lazy way is to call FindObjectOfType<LevelChanger>. This will search through the entire scene for components of that type, and will return your LevelChanger instance. I don't recommend this because it's pretty lazy and inefficient. This is only needed if one or both of your objects exist at run-time but not at edit-time
Another way is to treat LevelChanger like a singleton.
add a public static LevelChanger Instance; field to your LevelChanger. Then, on Awake() set Instance to this. ie Instance = this;
public static LevelChanger Instance;
public void Awake()
{
Instance = this;
}
Then, in any other script, you can call LevelChanger.FadeOut(false);
This is a similarly lazy way, but not quite inefficient. It does make your code a little harder to follow and there are a lot of devs that have problems with static instances like this. Just so you know.
One other way is to create an instance of the prefab using GameObject.Instantiate() and calling the function after getting a reference to the component. This is a bit more involved, but it might be a cleaner way for you.
There are two ways to do this, so lets do the cleanest way. In your project, create a folder called Resources if you don't already have one. Resources is a name unity will specifically look for when calling Resources.Load(). Drag your prefab object into that folder to create a new prefab. Call it LevelChanger
//Spawn the prefab gameobject
GameObject gameObject = GameObject.Instantiate(Resources.Load("LevelChanger")) as GameObject;
//Get a reference to its component LevelChanger
LevelChanger levelChanger = gameObject.GetComponent<LevelChanger>();
//Call the function
levelChanger.FadeOut(false);
After your prefab finishes the fade, you can have it destroy itself via Destroy(gameObject);
Finally, since you've indicated both scripts exist at edit time, you can simply add an inspector reference. This is the easiest way, but don't go crazy with inspector references. It makes the code difficult to follow.
public LevelChanger levelChanger;
Quick and dirty because little has been shared about the code and scene in question.
In Awake, get a reference to the gameObject with the already existing LevelChanger on it:
GameObject levelChangerGO = GameObject.Find("LevelChanger");
levelChanger = levelChangerGO.GetComponent<LevelChanger>();
then calling like this in an IEnumerator:
levelChanger.FadeOut(true); // line 178

Trouble instantiating prefab object by calling function in external script (Unity/C#)

I am having trouble getting one script to call an instantiation function from another script. The following image illustrates how I have my game objects set up.
I have 2 object things, Thing1 and Thing2, which move around the screen based on a movement script that is attached to each of them. Thing1 also has attached a reaction script. When Thing1 and Thing2 collide, Thing 3 should appear. Thing3 has an instantiation script attached to it that contains a function with an instantiation command.
When I call the instantiation function within the instantiation script (I put it in the Start function of Thing3), it works fine. However, when I take it out of there and try to put it in the Start function of the reaction script attached to Thing2 I cannot get it to work. Most currently, I get no error on compiling but as soon as the game starts I get is the following (The behavior of Thing1 and Thing2 also seems to be adversely affected):
"NullReferenceException: Object reference not set to an instance of an object"
When I look up the reason for that error I find that the most typical reason for that is a prefab object not being attached to the script. However, I DO have a prefab attached to the script. It is attached to the instantiation script that contains the instantiation function.
Below is the code for the instantiation script and the reaction script that is calling the instantiation function within the instatiation script.
//Instantiation
public class Thing3Instantiation : MonoBehaviour
{
public GameObject thing3Obj;
void Start()
{
//CreateThing3();
}
public void CreateThing3()
{
Instantiate(thing3Obj);
}
}
And
//Reaction
public class Reaction : MonoBehaviour
{
private Thing3Instatiation thing3instantiation;
void Awake()
{
thing3instantiation = GetComponent<Thing3Instantiation>();
}
void Start()
{
thing3instantiation.CreateThing3(); //This line triggers the null error
}
Any ideas on what I'm doing wrong?
If I understand your setup correctly:
Reaction is on Thing1
Thing3Instantiation is on Thing3
So when Reaction awakes, it tries to find the component called Thing3Instantiation on its own gameObject (Thing1). But it isn't on Thing1 it is on Thing3, so GetComponent returns null.
Hence, your NullReferenceException.
Your problem is actually at the line of Instantiate(thing3Obj);.
In other words... When you Instantiate a script, you won't have variables assigned, in your case thing3Obj. No matter if you've assigned them through Inspector, when Instanciated, you get a clean copy of that class, unallocated. You need to assign it before calling, that's why it's giving the Null exception.

How to reach to the child of a game object using FindGameObjectWithTag

In the hierarchy, I have a parent Game Object (named "parent"), which has a child (named "child" and tagged "John"). Initially, The parent is active but the child is inactive.
The parent has a script component, in the Update() function of which I have:
GameObject go = GameObject.FindGameObjectWithTag("John");
go.SetActive(true);
It doesn't work and I get "NullReferenceException: Object reference not set to an instance of an object" in the console. Even if the child is initially activated and I want to deactivate it, it still doesn't work and I get the same error. Why? And is it a good idea to use FindGameObjectWithTag() in Update() in the first place?
In Unity, you can't get an inactive GameObject using any Find function unfortunately.
If you're not creating your GameObject go dynamically, use a public variable and assign it via inspector, it's way better performance wise (as Unity probably uses references), and it's easier (imagine you change a tag, you won't need to check in all your code references to that tag)
As pointed out by Jichael, you cannot use Find functions to get inactive gameobjects.
You can however use:
var yourComponent = GetComponentInChildren<YourComponentType>(true);
If your child object has any component attached to it, you could easilly get it like this. Note the true (boolean) parameter. This is used to indicate that you want to check for inactive gameobjects.
A more detailed example:
var spriteRenderer = GetComponentInChildren<SpriteRenderer>(true);
spriteRenderer.Sprite = yourSpriteVariable;
spriteRenderer.gameObject.SetActive(true);
This is also better performant than using Find functionality, since you only need to look through the child objects of the calling behaviour.

Unity3D C# - Text UI

I have the following piece of code. I referenced the text component in the editor to no_lives. The gamemanager (singleton) is being instantiated a scene before. The debug.log() shows 5 in the console. But when I try to set the text I get that the reference is not set to an instance of the object. Why is that?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class level1_script : MonoBehaviour {
public Text no_lives;
// Use this for initialization
void Start () {
no_lives = GetComponent<Text>();
}
// Update is called once per frame
void Update () {
int lives_n = gamemanager.lives_f ();
Debug.Log (lives_n);
no_lives.text = lives_n + " x";
}
}
Are you certain that Start() was called before Update and that the GetComponent is actually setting a value to no_lives?
If not then code defensively. Also lives_n needs to be converted to a string lives_n.ToString() + " x" or use a format.
void Update () {
int lives_n = gamemanager.lives_f ();
Debug.Log (lives_n);
if(no_lives != null) {
no_lives.text = string.Format("{0} x", lives_n);
}
}
EDIT:
But when I try to set the text I get that the reference is not set to
an instance of the object.
That's because when you do GetComponent<Text>‌​(), it will look for the instance of Text component on the-same GameObject your level1_script script is attached to.
You have two choices:
1.Attach Text component that GameObject your level1_script script is atached to then your current code should work.
2.If you already have the Text component attached to another GameObject, let's say an Object called "MyText", use GameObject.Find to find "MyText" then perform GetComponent on it to get the Text component.
no_lives = GameObject.Find("MyText").GetComponent<Text>‌​()
Edit:
Well, both Text and script are attached to the same object
prntscr.com/f7p7vx
It's likely attached to multiple GameObject and the other GameObject does not have the Text script attached to it.
Select the level1_script script, go to Assets --> Find References in Scene then remove the duplicated script from other objects.
As the script logs '5' in the debug log, it seems to me that the variable lives_n is used correctly.
My guess is that the component "no_lives" isn't filled (that's what is often causing the "reference is not set to an instance of the object" error)
I suggest that you try to add the script to the UI Text object in your scene, this way the script finds the "Text" component on that object.
It's important to know the differences between objects and components, because they're both essential parts of unity. Objects are "things" in your scene, like cubes, cameras and stuff like that. Components on the other hand are part of object and define the properties of that object. For example: a camera object has a camera component to make it a camera and to make it actually viewable by the user.
The GetComponent<>() method searches for a (in your case) Text component on the object the script is attached to.
Hope this helps!
(oh and don't forget to remove the script from the other object you've placed it on, in case this works. Otherwise you'll still get errors)

Categories