Unity Runtime Script Instantiation with Default values - c#

I couldn't find any topic matching my scenario, so I made a new one.
I have a prefab of type Light called rewardHighlight.
I have a script called Reward which has a parameter Highlight and instantiates the Light using Light rewardHighlight = (Light) Instantiate (Highlight);.
I used drag and drop to set the default value of Highlight parameter of said script to RewardHighlight prefab.
When I drag and drop the script onto an object, it works correctly.
When I add Reward script at runtime using gameObject.AddComponent<Reward>(); I'm getting The thing you want to instantiate is null.
All help much appreciated!
#update
Full code (stripped for clarity):
Reward2.cs
using UnityEngine;
using System.Collections;
public class Reward2 : MonoBehaviour {
public Light Highlight;
private Light rewardHighlight;
// Use this for initialization
void Start () {
rewardHighlight = (Light) Instantiate (Highlight);
rewardHighlight.transform.position = new Vector3(transform.position.x, transform.position.y + 1, transform.position.z);
}
// Update is called once per frame
void Update () {
}
}
Test.cs
using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour {
// Use this for initialization
void Start () {
gameObject.AddComponent<Reward2>();
}
// Update is called once per frame
void Update () {
}
}
It looks like Unity scripts 'forget' their defaults at runtime.

After you call AddComponent in Test.Start you need to assign a value to the Highlight field on the Reward2 instance returned by AddComponent.
For example, your Test.Start may look a little like this:
void Start()
{
Reward2 obj = gameObject.AddComponent<Reward2>();
obj.Highlight = <SOME VALUE>;
}
Now, the value assigned to Highlight cannot simply be an instance of the Light component, e.g. obj.Highlight = new Light(). The reason for this is the Instantiate method creates an instance of a game object and not an individual component. If you use AddComponent<T> it still creates an instance of a game object, but will automatically locate component of type T on the new game object and will return that.
So, in order to make this work you can change the Test class to either:
1) Expose a field of type Light, similar to the Reward2 class, and assign that field to the Highlight property:
public Light HighlightPrefab;
...
void Start()
{
Reward2 obj = gameObject.AddComponent<Reward2>();
obj.Highlight = HighlightPrefab;
}
or
2) create a game object in the Start method, add a light component to it and then assign that component to the Highlight field:
void Start()
{
Reward2 obj = gameObject.AddComponent<Reward2>();
GameObject gameObjectForLight = new GameObject();
Light newLight = gameObjectForLight.AddComponent<Light>();
// Initialize newLight here
obj.Highlight = newLight;
}
In either case when Reward2.Start calls Instantiate it will see that the Highlight field is a component, it will then get the game object to which the component is attached and it will create a new instance of that game object.

Related

I cant assign my GameObject variable. Please assist

I am trying to assign ScriptManager to ObjectManager, and used the line :
ObjectManager = GameObject.Find("ScriptManager");
I have checked multiple times to make sure "ScriptManager" is spelt correct, Ive even tried copy pasting the name straight from Unity.
I recieve this error when running:
"UnassignedReferenceException: The variable ObjectManager of Mining has not been assigned.
You probably need to assign the ObjectManager variable of the Mining script in the inspector.
UnityEngine.GameObject.GetComponent[T] () (at <3be1a7ff939c43f181c0a10b5a0189ac>:0)
Mining.Sell () (at Assets/Mining.cs:49)"
I sadly cant assign the variable straight from the inspector, because the object with the code attached is loaded using a Prefab.
here is the full code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Mining : MonoBehaviour
{
public GameObject ObjectManager;
public Text WorkerCountText;
public float MiningSpeed;
public float WorkersInMine;
public float MiningMultiplyer;
public Collider2D collider;
public GameObject DropDown;
private float WorkerCount;
private float MineWorth;
private float Cash;
// Start is called before the first frame update
void Start()
{
ObjectManager = GameObject.Find("ScriptManager");
collider = GetComponent<Collider2D>();
DropDown.SetActive(false);
}
// Update is called once per frame
void Update()
{
Cash = ObjectManager.GetComponent<GenerateItems>().Money;
WorkerCount = ObjectManager.GetComponent<GenerateItems>().Workers;
MineWorth = ObjectManager.GetComponent<GenerateItems>().MineCost;
WorkerCountText.text = "Workers:" + WorkerCount;
}
public void Sell()
{
ObjectManager.GetComponent<GenerateItems>().Money = Cash + MineWorth;
Object.Destroy (this);
}
}
I would change the way how you assign the ScriptManager to ObjectManager. The function Find() is not very reliable and in case if anything changes in hierarchy or in the name of the GameObject, the function will not find the desired GameObject.
There are multiple ways how you can assign the GameObject to the ObjectManager, including more reliable function FindObjectOfType(), or simply creating variable:
public GameObject objectManager;
and dragging your ObjectManager from the hierarchy into the inspector in the ScriptManager GameObject.
Note: If you want to use any function of the ObjectManager you would need to get that component in the Start function of the ScriptManager.
Try using Object.FindObjectOfType
https://docs.unity3d.com/ScriptReference/Object.FindObjectOfType.html
I have discovered how to fix it!
For anyone wondering, the object this is attacked to is a prefab. And is only loaded once the player purchases the item. Meaning it is not loaded when Start() is executed. Moving the line into Sell() , Add() , and remove assigns it when the buttons are clicked. This fixed the error. Thank you to everyone who tried to help, I am now also using FindGameOBjectWithTag instead of Find to make sure it always works

'Component.GetComponent<T>()' is a method, which is not valid in the given context [Assembly-CSharp]csharp(CS0119)

When I try to get a variable from another script I get the error 'Component.GetComponent<T>()' is a method, which is not valid in the given context [Assembly-CSharp]csharp(CS0119)
The code that's throwing the error is here
GetComponent<EnemyAI>.Attack();
Any help or suggestions is greatly appreciated,
In fact, you can't get the component class just by writing the type in any code, and to do this you need to have an instance of the enemyAI gameObject as a reference. In this solution The easiest way to do this is to use GameObject.FindObjectOfType<>() like the code below:
public EnemyAI enemyAI; // define a variable for enemyAI
public void Start()
{
enemyAI = FindObjectOfType<EnemyAI>(); // it will find your enemyAI script gameObject
}
Once the game is begining, EnemyAI is stored in that variable, and you have access to the class properties. And you can easily run the methods.
private void OnNearEnemy()
{
enemyAI.Attack(); // call attack function
enemyAI.power += 10f; // add some power for example...
}
Now if you have another component like Rigidbody or Animator in EnemyAI, you can easily call and access it with GetComponent<>().
public void ForceEnemyToEscape()
{
var enemyAgent = enemyAI.GetComponent<NavMeshAgent>(); // get nav mesh
enemyAgent.destination = transform.position + transform.forward * 10f;
}
However, I suggest you learn object-oriented programming topics well. Because many calls can be made inside the enemy class. I hope it helped.

transfering a transform variable between two scritps in unity

public class player_movement : MonoBehaviour
{
// Start is called before the first frame update
public Transform position;
void Start()
{
position = GetComponent<Transform>();
}
how do I acsess the position variable in another script
There is no reason for any of this at all.
Your component player_movement is of type MonoBehaviour which is a Behaviour which is a Component and therefore already has the inherited property transform returning the Transform reference if the GameObject your component is attached to.
So whoever has a reference to your component immediately also has access to its .transform.position anyway.
As to how get that reference, there are thousands of different ways. The most typical ones
public player_movement playerMovement;
simply drag and drop it in vis the Inspector.
On another component attached to the same GameObject use
var playerMovement = GetComponent<player_movement>();
or if there is only one in the scene anyway
var playerMovement = FindObjectOfType<player_movement>();
either way in the end as said you can simply use
var playerTransform = playerMovement.transform;
and do with it whatever you want like accessing its position
var playerPosition = playerTransform.position;

Unity's GetComponent by string (or by some other method being passed from another script)

I'm trying to use as few scripts as possible, so I'm instantiating (example:) Button with buttonScript prefab from Spawner with spawnerScript. On Button being clicked (detected in buttonScript) I want a specific function to be called in spawnerScript - but without Spawner or spawnerScript being defined by user in buttonScript. They can be injected into this script, but cannot be typed out in script or defined in Unity's editor. How do I do this?
tl;dr - how do I call a function in other script without typing out script's name in GetComponent manually? Injecting target script for GetComponent is what I'm looking for.
You can pass a delegate to the Button
public class Spawner : MonoBehaviour{
void Start(){
GameObject btn = Instantiate<GameObject>(btnPrefab);
btn.GetComponent<Button>().onClick.AddListener(Method);
}
void Method(){}
}
EDIT based on comment:
"button contains a very simple script holding only a single GameObject (assigned at instantiation)"
So you're passing the GameObject at instantiation. Let's consider in the following example that this target is given from GetTarget() (I dunno how you get it)
void Start()
{
for(int i = 0 ; i < 100; i++)
{
GameObject btn = Instantiate<GameObject>(btnPrefab);
GameObject target = GetTarget();
btn.GetComponent<Button>().onClick.AddListener(()=>
{
Method(target);
});
}
}
void Method(GameObject obj)
{
Debug.Log(obj.name);
}
The concept to grab here is the lambda expression with the arrow. This can be understood as a method with no name that is only used in there (since it has no name you can’t call it elsewhere). That method returns void and has no parameter so it matches the Button listener requirements. In that nameless method you call another method that takes the GameObject target. Here a closure is created so even the Button object does not know about the target, it finds it.
Not so simple at first but you’ll get it.
Make your Spawner script static so there is always only one and you can access it with the Class instead of a variable version.
then instead of
GameObject.GetComponent().doMethod();
its
Spawner.doMethod();

Change color of multiple game objects in Unity 5

I would like to change the color of multiple gameobjects in Unity using a single script. I'm kinda lost in the way how to do it. I'm new to Unity and this is some sort of basic training for me.
Unity version: 5.3.4
Observed Behavior:
Added the same script to the other gameobjects and all change to the same color
Expected Behavior:
Change the color of the gameobjects individually
List of things tried:
Using the -FindGameObject-
Tried to acces the materials using the -GameObject-
Tried both at the same time
Thinking in multiple scripts to achieve the results I want
Here's the code
C#:
using UnityEngine;
using System.Collections;
public class ChangeColor : MonoBehaviour
{
//If I change these variables to -GameObject-
//It blocks me to access the renderer
//Making the variables public doesn't work either
private Renderer cube;
private Renderer sphere;
void Start ()
{
//Tried here the -FindGameObjectWithTag-
cube = GetComponent<Renderer>();
sphere = GetComponent<Renderer>();
}
void Update ()
{
if(Input.GetKeyDown(KeyCode.A))
{
//Tried here the -FindGameObjectWithTag-
cube.material.color = Color.red;
}
if(Input.GetKeyDown(KeyCode.S))
{
//Tried here the -FindGameObjectWithTag-
sphere.material.color = Color.green;
}
}
}
Maybe I'm doing something wrong, as I said I'm new to Unity, I kindly accept any help, if it is noobfriendly the better.
Thanks
There are a couple different ways you can go about doing this.
If they are all under the same parent without other objects there too you could loop through the children and change their colors
GameObject Parent = GameObject.Find("ParentObject");
for( int x = 0; x > Parent.transform.childCount; x++);
{
Parent.transform.GetChild(x).GetComponent<Renderer>().material.color = Color.red;
}
If there are less than say 20ish, you could create a list and simply drag and drop each transform into the inspector after changing the list's size to the number of objects you have.
/*Make sure you have Using "System.Collections.Generic" at the top */
//put this outside function so that the inspector can see it
public List<Transform> Objs;
// in your function put this (when changing the color)
foreach(Transform Tform in Objs){
Tform.GetComponent<Renderer>().material.color = Color.red;
}
Similarly to 2, if you want to do it all in code you can do
//Outside function
public List<Transform> Objs;
//inside function
Objs.Add(GameObject.Find("FirstObject").transform);
Objs.Add(GameObject.Find("SecondObject").transform);
//... keep doing this
//now do the same foreach loop as in 2
You could search by tag (if you have a lot of objects) (i would imagine that this would take a bit longer just because it is going through each component but I have no evidence to back that up)
//Outside function
public GameObject[] Objs;
//inside function
Objs = GameObject.FindGameObjectsWithTag("ATagToChangeColor");
foreach(Transform Tform in Objs){
Tform.GetComponent<Renderer>().material.color = Color.red();
}
And about this comment:
//If I change these variables to -GameObject-
//It blocks me to access the renderer
//Making the variables public doesn't work either
If you make them of type GameObject then you should easily be able to access the renderer simply by doing this:
public GameObject cube;
public void Start(){
cube.GetComponent<Renderer>().material.color = Color.red();
}
And making the variables public allows unity's inspector to see the variables so that you can change them in the unity editor without having to open the script.

Categories